diff --git a/.env.sample b/.env.sample index 7baff038..1c88fc72 100644 --- a/.env.sample +++ b/.env.sample @@ -4,6 +4,8 @@ SEPOLIA_RPC="https://rpc.ankr.com/eth_sepolia/" ARBITRUM_SEPOLIA_RPC="https://rpc.ankr.com/arbitrum_sepolia" OPTIMISM_SEPOLIA_RPC="https://rpc.ankr.com/optimism_sepolia" BASE_SEPOLIA_RPC="https://rpc.ankr.com/base_sepolia" +INTEROP_ALPH_0_RPC="https://interop-alpha-0.optimism.io" +INTEROP_ALPH_1_RPC="https://interop-alpha-1.optimism.io" # EVMx key addresses # Find the most up to date addresses in deployments/dev_addresses.json diff --git a/.gitmodules b/.gitmodules index dbd0a97b..53f75531 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "lib/solady"] path = lib/solady url = https://github.com/vectorized/solady +[submodule "lib/optimism"] + path = lib/optimism + url = https://github.com/ethereum-optimism/optimism diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index 156f98bd..2fd3035d 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -56,11 +56,13 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin _setAddressResolver(addressResolver_); sbType = FAST; } + /// @notice Sets the switchboard type /// @param sbType_ The switchboard type function _setSbType(bytes32 sbType_) internal { sbType = sbType_; } + /// @notice Creates a contract ID /// @param contractName_ The contract name /// @return bytes32 The contract ID diff --git a/contracts/interfaces/ISocket.sol b/contracts/interfaces/ISocket.sol index d46f6eca..2b775c0a 100644 --- a/contracts/interfaces/ISocket.sol +++ b/contracts/interfaces/ISocket.sol @@ -64,6 +64,12 @@ interface ISocket { bytes payload; } + enum ExecutionStatus { + NotExecuted, + Executed, + Reverted + } + /** * @notice To call the appGateway on EVMx. Should only be called by a plug. * @param payload_ bytes to be delivered to the Plug on EVMx @@ -99,4 +105,6 @@ interface ISocket { function getPlugConfig( address plugAddress_ ) external view returns (address appGateway, address switchboard); + + function payloadExecuted(bytes32 payloadId_) external view returns (ExecutionStatus); } diff --git a/contracts/interfaces/ISwitchboard.sol b/contracts/interfaces/ISwitchboard.sol index 394d4044..151a8389 100644 --- a/contracts/interfaces/ISwitchboard.sol +++ b/contracts/interfaces/ISwitchboard.sol @@ -7,6 +7,17 @@ pragma solidity ^0.8.21; * different blockchain networks. */ interface ISwitchboard { + struct PayloadParams { + bytes32 payloadId; + address appGateway; + address transmitter; + address target; + uint256 value; + uint256 deadline; + uint256 executionGasLimit; + bytes payload; + } + /** * @notice Checks if a packet can be allowed to go through the switchboard. * @param digest_ the packet digest. @@ -16,4 +27,10 @@ interface ISwitchboard { function allowPacket(bytes32 digest_, bytes32 packetId_) external view returns (bool); function attest(bytes32 payloadId_, bytes32 digest_, bytes calldata proof_) external; + + function syncOut( + bytes32 digest_, + bytes32 payloadId_, + PayloadParams calldata payloadParams_ + ) external; } diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 0a54fa6a..9abb3b73 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -22,13 +22,13 @@ interface IWatcherPrecompile { /// @dev Only callable by authorized addresses function setOnChainContracts( uint32 chainSlug_, - bytes32 sbType_, - address switchboard_, address socket_, address contractFactoryPlug_, address feesPlug_ ) external; + function setSwitchboard(uint32 chainSlug_, bytes32 sbType_, address switchboard_) external; + /// @notice Retrieves plug configuration for a specific network and plug /// @param chainSlug_ The identifier of the network /// @param plug_ The address of the plug diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index bd70a175..d44af419 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -46,16 +46,10 @@ contract Socket is SocketUtils { //////////////////////////////////////////////////////////// uint64 public callCounter; - enum ExecutionStatus { - NotExecuted, - Executed, - Reverted - } - /** * @dev keeps track of whether a payload has been executed or not using payload id */ - mapping(bytes32 => ExecutionStatus) public payloadExecuted; + mapping(bytes32 => ExecutionStatus) public override payloadExecuted; constructor( uint32 chainSlug_, diff --git a/contracts/protocol/socket/SocketBatcher.sol b/contracts/protocol/socket/SocketBatcher.sol index 726101e8..fdff816d 100644 --- a/contracts/protocol/socket/SocketBatcher.sol +++ b/contracts/protocol/socket/SocketBatcher.sol @@ -5,6 +5,7 @@ import "solady/auth/Ownable.sol"; import "../../interfaces/ISocket.sol"; import "../../interfaces/ISwitchboard.sol"; import "../utils/RescueFundsLib.sol"; +import {ECDSA} from "solady/utils/ECDSA.sol"; import {AttestAndExecutePayloadParams} from "../../protocol/utils/common/Structs.sol"; /** @@ -27,7 +28,7 @@ contract SocketBatcher is Ownable { function attestAndExecute( AttestAndExecutePayloadParams calldata params_ - ) external payable returns (bytes memory) { + ) external payable returns (bytes memory returnData) { ISwitchboard(params_.switchboard).attest(params_.payloadId, params_.digest, params_.proof); ISocket.ExecuteParams memory executeParams = ISocket.ExecuteParams({ @@ -37,12 +38,37 @@ contract SocketBatcher is Ownable { deadline: params_.deadline, payload: params_.payload }); - return - socket__.execute{value: msg.value}( - params_.appGateway, - executeParams, - params_.transmitterSignature - ); + + returnData = socket__.execute{value: msg.value}( + params_.appGateway, + executeParams, + params_.transmitterSignature + ); + + address transmitter = _recoverSigner( + keccak256(abi.encode(address(socket__), params_.payloadId)), + params_.transmitterSignature + ); + ISwitchboard.PayloadParams memory payloadParams = ISwitchboard.PayloadParams({ + payloadId: params_.payloadId, + appGateway: params_.appGateway, + transmitter: transmitter, + target: params_.target, + value: 0, + deadline: params_.deadline, + executionGasLimit: params_.executionGasLimit, + payload: params_.payload + }); + ISwitchboard(params_.switchboard).syncOut(params_.digest, params_.payloadId, payloadParams); + } + + function _recoverSigner( + bytes32 digest_, + bytes memory signature_ + ) internal view returns (address signer) { + bytes32 digest = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", digest_)); + // recovered signer is checked for the valid roles later + signer = ECDSA.recover(digest, signature_); } function rescueFunds(address token_, address to_, uint256 amount_) external onlyOwner { diff --git a/contracts/protocol/socket/switchboard/FastSwitchboard.sol b/contracts/protocol/socket/switchboard/FastSwitchboard.sol index 874e7d19..acbbce5b 100644 --- a/contracts/protocol/socket/switchboard/FastSwitchboard.sol +++ b/contracts/protocol/socket/switchboard/FastSwitchboard.sol @@ -38,7 +38,7 @@ contract FastSwitchboard is SwitchboardBase { * there can be multiple proposals for same digest. To avoid need to re-attest for different proposals * with same digest, we are storing attestations against digest instead of packetId and proposalCount. */ - function attest(bytes32 payloadId_, bytes32 digest_, bytes calldata proof_) external { + function attest(bytes32 payloadId_, bytes32 digest_, bytes calldata proof_) external virtual { address watcher = _recoverSigner(keccak256(abi.encode(address(this), digest_)), proof_); if (isAttested[digest_]) revert AlreadyAttested(); @@ -78,4 +78,10 @@ contract FastSwitchboard is SwitchboardBase { function registerSwitchboard() external onlyOwner { socket__.registerSwitchboard(); } + + function syncOut( + bytes32 digest_, + bytes32 payloadId_, + PayloadParams calldata payloadParams_ + ) external virtual {} } diff --git a/contracts/protocol/socket/switchboard/OpInteropSwitchboard.sol b/contracts/protocol/socket/switchboard/OpInteropSwitchboard.sol new file mode 100644 index 00000000..5619c087 --- /dev/null +++ b/contracts/protocol/socket/switchboard/OpInteropSwitchboard.sol @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; + +import {FastSwitchboard} from "./FastSwitchboard.sol"; +import {SuperchainEnabled} from "./SuperchainEnabled.sol"; +import {ISocket} from "../../../interfaces/ISocket.sol"; + +contract OpInteropSwitchboard is FastSwitchboard, SuperchainEnabled { + address public token; + address public remoteAddress; + uint256 public remoteChainId; + + mapping(bytes32 => bool) public isSyncedOut; + mapping(bytes32 => bytes32) public payloadIdToDigest; + mapping(address => uint256) public unminted; + + error OnlyTokenAllowed(); + + modifier onlyToken() { + if (msg.sender != token) revert OnlyTokenAllowed(); + _; + } + + constructor( + uint32 chainSlug_, + ISocket socket_, + address owner_ + ) FastSwitchboard(chainSlug_, socket_, owner_) { + if (chainSlug_ == 420120000) { + remoteChainId = 420120001; + } else if (chainSlug_ == 420120001) { + remoteChainId = 420120000; + } + } + + function attest(bytes32 payloadId_, bytes32 digest_, bytes calldata proof_) external override { + address watcher = _recoverSigner(keccak256(abi.encode(address(this), 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 syncOut( + bytes32 digest_, + bytes32 payloadId_, + PayloadParams calldata payloadParams_ + ) external override { + if (isSyncedOut[digest_]) return; + isSyncedOut[digest_] = true; + + if (!isAttested[digest_]) return; + + bytes32 digest = payloadIdToDigest[payloadId_]; + if (digest != digest_) return; + + bytes32 expectedDigest = _packPayload(payloadParams_); + if (expectedDigest != digest_) return; + + ISocket.ExecutionStatus isExecuted = socket__.payloadExecuted(payloadId_); + if (isExecuted != ISocket.ExecutionStatus.Executed) return; + + (address user, uint256 amount, bool isBurn) = _decodeBurn(payloadParams_.payload); + + if (!isBurn) return; + _xMessageContract( + remoteChainId, + remoteAddress, + abi.encodeWithSelector(this.syncIn.selector, user, amount) + ); + } + + function syncIn( + address user_, + uint256 amount_ + ) external xOnlyFromContract(remoteAddress, remoteChainId) { + unminted[user_] += amount_; + } + + function _decodeBurn( + bytes memory payload + ) internal pure returns (address user, uint256 amount, bool isBurn) { + // Extract function selector from payload + bytes4 selector; + assembly { + // Load first 4 bytes from payload data + selector := mload(add(payload, 32)) + } + // Check if selector matches burn() + if (selector != bytes4(0x9dc29fac)) return (user, amount, false); + + // Decode the payload after the selector (skip first 4 bytes) + assembly { + user := mload(add(add(payload, 36), 0)) // 32 + 4 bytes offset for first param + amount := mload(add(add(payload, 68), 0)) // 32 + 4 + 32 bytes offset for second param + } + isBurn = true; + } + + function _packPayload(PayloadParams memory payloadParams_) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + payloadParams_.payloadId, + payloadParams_.appGateway, + payloadParams_.transmitter, + payloadParams_.target, + payloadParams_.value, + payloadParams_.deadline, + payloadParams_.executionGasLimit, + payloadParams_.payload + ) + ); + } + + function checkAndConsume(address user_, uint256 amount_) external onlyToken { + unminted[user_] -= amount_; + } + + function setToken(address token_) external onlyOwner { + token = token_; + } + + function setRemoteAddress(address _remoteAddress) external onlyOwner { + remoteAddress = _remoteAddress; + } + + function setRemoteChainId(uint256 _remoteChainId) external onlyOwner { + remoteChainId = _remoteChainId; + } +} diff --git a/contracts/protocol/socket/switchboard/SuperchainEnabled.sol b/contracts/protocol/socket/switchboard/SuperchainEnabled.sol new file mode 100644 index 00000000..21105805 --- /dev/null +++ b/contracts/protocol/socket/switchboard/SuperchainEnabled.sol @@ -0,0 +1,73 @@ +pragma solidity ^0.8.13; + +// SuperchainEnabled provides utilities for cross-chain event validation, +// sending messages, and receiving messages with modifiers. + +import {IL2ToL2CrossDomainMessenger} from "optimism/interfaces/L2/IL2ToL2CrossDomainMessenger.sol"; +import {Predeploys} from "optimism/src/libraries/Predeploys.sol"; + +abstract contract SuperchainEnabled { + // Error definitions + error CallerNotL2ToL2CrossDomainMessenger(); + error InvalidCrossDomainSender(); + error InvalidSourceChain(); + + /// @notice Sends a cross-chain message to a destination address on another chain + /// @param destChainId The chain ID of the destination chain + /// @param destAddress The address of the destination contract + /// @param data The calldata to send to the destination contract + function _xMessageContract( + uint256 destChainId, + address destAddress, + bytes memory data + ) internal { + IL2ToL2CrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER).sendMessage( + destChainId, + destAddress, + data + ); + } + + /// @notice Checks if the cross-domain message is from the expected source + /// @param expectedSource The expected source address + /// @return bool True if the message is from the expected source, false otherwise + function _isValidCrossDomainSender(address expectedSource) internal view returns (bool) { + if (msg.sender != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) { + return false; + } + return + IL2ToL2CrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) + .crossDomainMessageSender() == expectedSource; + } + + /// @notice Modifier to validate messages from a specific address + /// @param expectedSource The expected source address + modifier xOnlyFromAddress(address expectedSource) { + if (!_isValidCrossDomainSender(expectedSource)) { + revert InvalidCrossDomainSender(); + } + _; + } + + /// @notice Modifier to validate messages from a specific address on a specific chain + /// @param expectedSource The expected source address + /// @param expectedChainId The expected source chain ID + modifier xOnlyFromContract(address expectedSource, uint256 expectedChainId) { + if (msg.sender != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) { + revert CallerNotL2ToL2CrossDomainMessenger(); + } + if ( + IL2ToL2CrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) + .crossDomainMessageSender() != expectedSource + ) { + revert InvalidCrossDomainSender(); + } + if ( + IL2ToL2CrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) + .crossDomainMessageSource() != expectedChainId + ) { + revert InvalidSourceChain(); + } + _; + } +} diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index ca5e6fac..a3481e4b 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -25,15 +25,11 @@ abstract contract WatcherPrecompileConfig is WatcherPrecompileLimits { /// @notice Emitted when contracts are set for a network /// @param chainSlug The identifier of the network - /// @param sbType The type of switchboard - /// @param switchboard The address of the switchboard /// @param socket The address of the socket /// @param contractFactoryPlug The address of the contract factory plug /// @param feesPlug The address of the fees plug event OnChainContractSet( uint32 chainSlug, - bytes32 sbType, - address switchboard, address socket, address contractFactoryPlug, address feesPlug @@ -67,28 +63,26 @@ abstract contract WatcherPrecompileConfig is WatcherPrecompileLimits { /// @notice Sets the switchboard for a network /// @param chainSlug_ The identifier of the network - /// @param switchboard_ The address of the switchboard function setOnChainContracts( uint32 chainSlug_, - bytes32 sbType_, - address switchboard_, address socket_, address contractFactoryPlug_, address feesPlug_ ) external override onlyOwner { - switchboards[chainSlug_][sbType_] = switchboard_; sockets[chainSlug_] = socket_; contractFactoryPlug[chainSlug_] = contractFactoryPlug_; feesPlug[chainSlug_] = feesPlug_; - emit OnChainContractSet( - chainSlug_, - sbType_, - switchboard_, - socket_, - contractFactoryPlug_, - feesPlug_ - ); + emit OnChainContractSet(chainSlug_, socket_, contractFactoryPlug_, feesPlug_); + } + + function setSwitchboard( + uint32 chainSlug_, + bytes32 sbType_, + address switchboard_ + ) external override onlyOwner { + switchboards[chainSlug_][sbType_] = switchboard_; + emit SwitchboardSet(chainSlug_, sbType_, switchboard_); } // @dev app gateway can set the valid plugs for each chain slug diff --git a/deployments/dev_addresses.json b/deployments/dev_addresses.json index 242369d1..1ceda99f 100644 --- a/deployments/dev_addresses.json +++ b/deployments/dev_addresses.json @@ -1,32 +1,32 @@ { - "421614": { - "ContractFactoryPlug": "0xCE9aF7Fb0Ee31276Ea64Cd7414d54eC57dA9b675", - "FastSwitchboard": "0x772088727d003f5A6Cf816E745ba6B8F8dEBbCe9", - "FeesPlug": "0xcc38176545fa5f6c2aB970F6d2D62b2D90bBA3Ba", - "Socket": "0xdE177c392Ad47377BA944215A944f530F79E1d87", - "SocketBatcher": "0x6BF08F11bDA6CD571Bc942F23ec38b8652FFfB2D", - "startBlock": 127798344 - }, "7625382": { - "AddressResolver": "0x403eCBcC4d4bB8Fad09034bf0d197dDC626C832f", - "AddressResolverImpl": "0xbAeF84edEae864Ff22Bd9c9912AdfF84aD490d82", - "AuctionManager": "0x7dc45C49650e2914e3fA6E194e2080775c58256E", - "AuctionManagerImpl": "0xa07e38cAB46eAA358C3653C63219f1009e8F7789", - "DeliveryHelper": "0x0861f0888125e5A243C7af2c3E0F80C357c4c0C0", - "DeliveryHelperImpl": "0x9F10A0c71178dbD4d049f2C04fD0e34966134b9e", - "ERC1967Factory": "0x945e9ab4c08c225C10F178a0bd600BcC2bA7Cc78", - "FeesManager": "0x777fAAf1c30Ce8E68262b1cbF0a752d4f1bA652C", - "FeesManagerImpl": "0xB423eE3bffc3604F96B59cF419C48AE05b8E9d0b", - "startBlock": 73062, - "WatcherPrecompile": "0xadA397123A6E80F67b1887895A62B2D9273E50b4", - "WatcherPrecompileImpl": "0x71956F006Ec5434581D3Fd5E7224BB3bae231907" + "AddressResolver": "0x26FDDCE85557c98C20C7C5077dF44e6f239eDdFD", + "AddressResolverImpl": "0x4106c821Cc5119cE276A58380FD3CD18469c976f", + "AuctionManager": "0xB16d95D5Fd6Ccf80895427455EC9AfEfD2729C99", + "AuctionManagerImpl": "0xcc9486bce931041d1393921b32f7b4C60F6eAA99", + "DeliveryHelper": "0x8be1C218D5e2572D1d324be4833Ee9B62fBFA2ee", + "DeliveryHelperImpl": "0x30b5Cf86f186354C3C82C6b987F1Acb43f420a65", + "ERC1967Factory": "0x311fA7550C52c59AB3Dc3c18Fb11aa566d4b35B9", + "FeesManager": "0x9a2CeEc756013b71C3604C17d7A0B1Ae62E26767", + "FeesManagerImpl": "0xe519FEa99893BFF9334EF0B5253ea330bB73A7Fd", + "startBlock": 497366, + "WatcherPrecompile": "0xB15E7B893E3Ef6819A3570154B75d3A5AeF0081B", + "WatcherPrecompileImpl": "0x5B7600B2D9B14a5Ff79dd20845A88370D3F1Bf74" + }, + "420120000": { + "ContractFactoryPlug": "0xee1Aef0b06f63Aa1c881838794Dd0876462c2B0d", + "FastSwitchboard": "0x9EDfb162b725CF6d628D68af200cAe8b624111eD", + "FeesPlug": "0x5F77550E3072c913A20B2fbdAb14026fe0E8B450", + "Socket": "0x5e1641B190B71ECCc85b1ECe934F31cD9b3dcF7a", + "SocketBatcher": "0xaC61f5696e0E2636dA7bD69827380f2Ab41A3C38", + "startBlock": 1070887 }, - "11155420": { - "ContractFactoryPlug": "0x289A0413420f812a7b05F931FB0726168121ae5a", - "FastSwitchboard": "0x59D9c8C5515cF9C8A9c83458E3D78C2a246E3e7C", - "FeesPlug": "0x285d1b2e93c1c74E141dC37f759B8aFAcD479b2b", - "Socket": "0xa347A8475d4d218b22e8b0cc90FF76B3e6c8043c", - "SocketBatcher": "0x4a7Ccf2845222172A8B7Fc6E132eDb64cCB4E4a4", - "startBlock": 24425961 + "420120001": { + "ContractFactoryPlug": "0xee1Aef0b06f63Aa1c881838794Dd0876462c2B0d", + "FastSwitchboard": "0x9EDfb162b725CF6d628D68af200cAe8b624111eD", + "FeesPlug": "0x5F77550E3072c913A20B2fbdAb14026fe0E8B450", + "Socket": "0x5e1641B190B71ECCc85b1ECe934F31cD9b3dcF7a", + "SocketBatcher": "0xaC61f5696e0E2636dA7bD69827380f2Ab41A3C38", + "startBlock": 1070909 } } diff --git a/deployments/dev_verification.json b/deployments/dev_verification.json index 4c11da30..75b5366e 100644 --- a/deployments/dev_verification.json +++ b/deployments/dev_verification.json @@ -1,228 +1,158 @@ { - "84532": [ - [ - "0xA557EBE094F939ae6eE8F18c8F88D06182168786", - "ContractFactoryPlug", - "contracts/protocol/payload-delivery/ContractFactoryPlug.sol", - [ - "0x6c40Fb39B03e32EC4D23e31DdE6D10283F2C7b4F", - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" - ] - ], - [ - "0xD78f99D62BeaF0918bB0601C68EB537b6703Ce63", - "FeesPlug", - "contracts/protocol/payload-delivery/FeesPlug.sol", - [ - "0x6c40Fb39B03e32EC4D23e31DdE6D10283F2C7b4F", - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" - ] - ], - [ - "0x06234dB2D69Ac158793a3ce59c3764422028E964", - "FastSwitchboard", - "contracts/protocol/socket/switchboard/FastSwitchboard.sol", - [ - 84532, - "0x6c40Fb39B03e32EC4D23e31DdE6D10283F2C7b4F", - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" - ] - ], - [ - "0xA944BBe5D4F67a242C9e92d539fF2d55616283a7", - "SocketBatcher", - "contracts/protocol/socket/SocketBatcher.sol", - [ - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", - "0x6c40Fb39B03e32EC4D23e31DdE6D10283F2C7b4F" - ] - ], - [ - "0x6c40Fb39B03e32EC4D23e31DdE6D10283F2C7b4F", - "Socket", - "contracts/protocol/socket/Socket.sol", - [84532, "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", "EVMX"] - ] - ], - "421614": [ - [ - "0x29E632aE79107A82C637016CA21030d922De5375", - "FastSwitchboard", - "contracts/protocol/socket/switchboard/FastSwitchboard.sol", - [ - 421614, - "0xa0E1738a9Fc0698789866e09d7A335d30128C5C5", - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" - ] - ] - ], "7625382": [ [ - "0xa07e38cAB46eAA358C3653C63219f1009e8F7789", + "0xcc9486bce931041d1393921b32f7b4C60F6eAA99", "AuctionManager", "contracts/protocol/payload-delivery/AuctionManager.sol", [] ], [ - "0x9F10A0c71178dbD4d049f2C04fD0e34966134b9e", + "0x30b5Cf86f186354C3C82C6b987F1Acb43f420a65", "DeliveryHelper", "contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol", [] ], [ - "0xB423eE3bffc3604F96B59cF419C48AE05b8E9d0b", + "0xe519FEa99893BFF9334EF0B5253ea330bB73A7Fd", "FeesManager", "contracts/protocol/payload-delivery/FeesManager.sol", [] ], [ - "0x71956F006Ec5434581D3Fd5E7224BB3bae231907", + "0x5B7600B2D9B14a5Ff79dd20845A88370D3F1Bf74", "WatcherPrecompile", "contracts/protocol/watcherPrecompile/WatcherPrecompile.sol", [] ], [ - "0xbAeF84edEae864Ff22Bd9c9912AdfF84aD490d82", + "0x4106c821Cc5119cE276A58380FD3CD18469c976f", "AddressResolver", "contracts/protocol/AddressResolver.sol", [] ], [ - "0x945e9ab4c08c225C10F178a0bd600BcC2bA7Cc78", + "0x311fA7550C52c59AB3Dc3c18Fb11aa566d4b35B9", "ERC1967Factory", "lib/solady/src/utils/ERC1967Factory.sol", [] - ], - [ - "0x09B503e744DCB2cA2827ce5AF08Fd49Ba06D17e4", - "WatcherPrecompile", - "contracts/protocol/watcherPrecompile/WatcherPrecompile.sol", - [] - ], - [ - "0x6c73961Bfaa2c9c1d0D7F4C213aa85af15b7CB54", - "WatcherPrecompile", - "contracts/protocol/watcherPrecompile/WatcherPrecompile.sol", - [] - ], - [ - "0xd151bD217704F72f717C2111207e6Bb33B609f61", - "WatcherPrecompile", - "contracts/protocol/watcherPrecompile/WatcherPrecompile.sol", - [] - ], - [ - "0x8dcEE196AFECe27545687426914d2830ff2dbc35", - "FeesManager", - "contracts/protocol/payload-delivery/app-gateway/FeesManager.sol", - [] - ], - [ - "0xb1F4CbFCE786aA8B553796Fb06c04Dd461967A16", - "WatcherPrecompile", - "contracts/protocol/watcherPrecompile/WatcherPrecompile.sol", - [] - ], - [ - "0x23EF7Af3bC1009EA6f95c3389921d5cB19950182", - "AuctionManager", - "contracts/protocol/payload-delivery/app-gateway/AuctionManager.sol", - [] - ], - [ - "0x4CCF8F511A364827E5e6749b196BB26Ea00bF512", - "DeliveryHelper", - "contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol", - [] - ], + ] + ], + "420120000": [ [ - "0x39f9b492695375F703450c5653c9D80CFa38e6eD", - "FeesManager", - "contracts/protocol/payload-delivery/app-gateway/FeesManager.sol", - [] + "0x9EDfb162b725CF6d628D68af200cAe8b624111eD", + "OpInteropSwitchboard", + "contracts/protocol/socket/switchboard/OpInteropSwitchboard.sol", + [ + 420120000, + "0x5e1641B190B71ECCc85b1ECe934F31cD9b3dcF7a", + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" + ] ], [ - "0x234cA13f5dC4a2467c81E515F57238BF9f53156E", - "WatcherPrecompile", - "contracts/protocol/watcherPrecompile/WatcherPrecompile.sol", - [] + "0xee1Aef0b06f63Aa1c881838794Dd0876462c2B0d", + "ContractFactoryPlug", + "contracts/protocol/payload-delivery/ContractFactoryPlug.sol", + [ + "0x5e1641B190B71ECCc85b1ECe934F31cD9b3dcF7a", + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" + ] ], [ - "0xbBfb525ADc6eC38Ef0D4b807d327c2Dd1C286de1", - "AddressResolver", - "contracts/protocol/AddressResolver.sol", - [] + "0x5F77550E3072c913A20B2fbdAb14026fe0E8B450", + "FeesPlug", + "contracts/protocol/payload-delivery/FeesPlug.sol", + [ + "0x5e1641B190B71ECCc85b1ECe934F31cD9b3dcF7a", + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" + ] ], [ - "0x208dC31cd6042a09bbFDdB31614A337a51b870ba", - "ERC1967Factory", - "lib/solady/src/utils/ERC1967Factory.sol", - [] + "0xbDE0D2da12F703Ccd275d721214745BccDCAD124", + "OpInteropSwitchboard", + "contracts/protocol/socket/switchboard/OpInteropSwitchboard.sol", + [ + 420120000, + "0x5e1641B190B71ECCc85b1ECe934F31cD9b3dcF7a", + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" + ] ], [ - "0x4C68058509d754Cc0dE474eBC20aE94e4787E704", - "AuctionManager", - "contracts/protocol/payload-delivery/app-gateway/AuctionManager.sol", - [] + "0xaC61f5696e0E2636dA7bD69827380f2Ab41A3C38", + "SocketBatcher", + "contracts/protocol/socket/SocketBatcher.sol", + [ + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", + "0x5e1641B190B71ECCc85b1ECe934F31cD9b3dcF7a" + ] ], [ - "0x3c9f5172feb0dDfC06176cE67B566EFbb01CCe98", - "DeliveryHelper", - "contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol", - [] - ], + "0x5e1641B190B71ECCc85b1ECe934F31cD9b3dcF7a", + "Socket", + "contracts/protocol/socket/Socket.sol", + [ + 420120000, + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", + "EVMX" + ] + ] + ], + "420120001": [ [ - "0xF4D3BDe438416938217d4624c82AEbEb46CeD371", - "FeesManager", - "contracts/protocol/payload-delivery/app-gateway/FeesManager.sol", - [] + "0x9EDfb162b725CF6d628D68af200cAe8b624111eD", + "OpInteropSwitchboard", + "contracts/protocol/socket/switchboard/OpInteropSwitchboard.sol", + [ + 420120001, + "0x5e1641B190B71ECCc85b1ECe934F31cD9b3dcF7a", + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" + ] ], [ - "0x8F18fC10a8b40b548C6F04Fb252481c783A9ace0", - "WatcherPrecompile", - "contracts/protocol/watcherPrecompile/WatcherPrecompile.sol", - [] + "0xee1Aef0b06f63Aa1c881838794Dd0876462c2B0d", + "ContractFactoryPlug", + "contracts/protocol/payload-delivery/ContractFactoryPlug.sol", + [ + "0x5e1641B190B71ECCc85b1ECe934F31cD9b3dcF7a", + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" + ] ], [ - "0x8662FC08dEC7c61Dd3432fAF45a5bC024BB60B00", - "AddressResolver", - "contracts/protocol/AddressResolver.sol", - [] + "0x5F77550E3072c913A20B2fbdAb14026fe0E8B450", + "FeesPlug", + "contracts/protocol/payload-delivery/FeesPlug.sol", + [ + "0x5e1641B190B71ECCc85b1ECe934F31cD9b3dcF7a", + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" + ] ], [ - "0x234cA13f5dC4a2467c81E515F57238BF9f53156E", - "ERC1967Factory", - "lib/solady/src/utils/ERC1967Factory.sol", - [] - ] - ], - "11155111": [], - "11155420": [ - [ - "0x245C0DCF8eF6e52b4d03c96b563C83a5f78A1E14", - "FastSwitchboard", - "contracts/protocol/socket/switchboard/FastSwitchboard.sol", + "0xbDE0D2da12F703Ccd275d721214745BccDCAD124", + "OpInteropSwitchboard", + "contracts/protocol/socket/switchboard/OpInteropSwitchboard.sol", [ - 11155420, - "0x79Ac996De9333956f4980397ED5Bd91f77f10b01", + 420120001, + "0x5e1641B190B71ECCc85b1ECe934F31cD9b3dcF7a", "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" ] ], [ - "0xBf9Ec2b0441eeEA9bEb89C6638921c37c15A13E4", - "FastSwitchboard", - "contracts/protocol/socket/switchboard/FastSwitchboard.sol", + "0xaC61f5696e0E2636dA7bD69827380f2Ab41A3C38", + "SocketBatcher", + "contracts/protocol/socket/SocketBatcher.sol", [ - 11155420, - "0x79Ac996De9333956f4980397ED5Bd91f77f10b01", - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", + "0x5e1641B190B71ECCc85b1ECe934F31cD9b3dcF7a" ] ], [ - "0x79Ac996De9333956f4980397ED5Bd91f77f10b01", + "0x5e1641B190B71ECCc85b1ECe934F31cD9b3dcF7a", "Socket", "contracts/protocol/socket/Socket.sol", - [11155420, "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", "EVMX"] + [ + 420120001, + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", + "EVMX" + ] ] ] } diff --git a/deployments/prod_addresses.json b/deployments/prod_addresses.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/deployments/prod_addresses.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/hardhat-scripts/config/config.ts b/hardhat-scripts/config/config.ts index 6cea0d30..874fbb2e 100644 --- a/hardhat-scripts/config/config.ts +++ b/hardhat-scripts/config/config.ts @@ -24,10 +24,12 @@ export const logConfig = () => { }; export const chains: Array = [ - ChainSlug.ARBITRUM_SEPOLIA, - ChainSlug.OPTIMISM_SEPOLIA, + // ChainSlug.ARBITRUM_SEPOLIA, + // ChainSlug.OPTIMISM_SEPOLIA, // ChainSlug.SEPOLIA, - ChainSlug.BASE_SEPOLIA, + // ChainSlug.BASE_SEPOLIA, + ChainSlug.INTEROP_ALPHA_0, + ChainSlug.INTEROP_ALPHA_1, ]; export const EVM_CHAIN_ID_MAP: Record = { [DeploymentMode.LOCAL]: 7625382, diff --git a/hardhat-scripts/constants/enums.ts b/hardhat-scripts/constants/enums.ts index 371023e1..68e0de2a 100644 --- a/hardhat-scripts/constants/enums.ts +++ b/hardhat-scripts/constants/enums.ts @@ -2,6 +2,7 @@ export enum CORE_CONTRACTS { Socket = "Socket", SocketBatcher = "SocketBatcher", FastSwitchboard = "FastSwitchboard", + OpInteropSwitchboard = "OpInteropSwitchboard", FeesPlug = "FeesPlug", ContractFactoryPlug = "ContractFactoryPlug", } diff --git a/hardhat-scripts/deploy/1.deploy.ts b/hardhat-scripts/deploy/1.deploy.ts index 645e82f7..c7f2fe09 100644 --- a/hardhat-scripts/deploy/1.deploy.ts +++ b/hardhat-scripts/deploy/1.deploy.ts @@ -255,15 +255,26 @@ const deploySocketContracts = async () => { ); deployUtils.addresses[contractName] = batcher.address; - contractName = CORE_CONTRACTS.FastSwitchboard; - const sb: Contract = await getOrDeploy( + // contractName = CORE_CONTRACTS.FastSwitchboard; + // const sb: Contract = await getOrDeploy( + // contractName, + // contractName, + // `contracts/protocol/socket/switchboard/${contractName}.sol`, + // [chain as ChainSlug, socket.address, socketOwner], + // deployUtils + // ); + // deployUtils.addresses[contractName] = sb.address; + + contractName = CORE_CONTRACTS.OpInteropSwitchboard; + const opSwitchboard: Contract = await getOrDeploy( contractName, contractName, `contracts/protocol/socket/switchboard/${contractName}.sol`, [chain as ChainSlug, socket.address, socketOwner], deployUtils ); - deployUtils.addresses[contractName] = sb.address; + deployUtils.addresses[CORE_CONTRACTS.FastSwitchboard] = + opSwitchboard.address; contractName = CORE_CONTRACTS.FeesPlug; const feesPlug: Contract = await getOrDeploy( diff --git a/hardhat-scripts/deploy/2.roles.ts b/hardhat-scripts/deploy/2.roles.ts index 8aa12732..a9751dbb 100644 --- a/hardhat-scripts/deploy/2.roles.ts +++ b/hardhat-scripts/deploy/2.roles.ts @@ -22,6 +22,7 @@ import { ROLES } from "../constants/roles"; export const REQUIRED_ROLES = { FastSwitchboard: [ROLES.WATCHER_ROLE, ROLES.RESCUE_ROLE], + // OpInteropSwitchboard: [ROLES.WATCHER_ROLE, ROLES.RESCUE_ROLE], Socket: [ROLES.GOVERNANCE_ROLE, ROLES.RESCUE_ROLE], FeesPlug: [ROLES.RESCUE_ROLE], ContractFactoryPlug: [ROLES.RESCUE_ROLE], @@ -50,7 +51,7 @@ async function setRoleForContract( if (!hasRole) { let tx = await contract.grantRole(roleHash, targetAddress, { - ...overrides(chain), + ...await overrides(chain), }); console.log( `granting ${roleName} role to ${targetAddress} for ${contractName}`, @@ -88,7 +89,8 @@ async function setRolesForOnChain( for (const roleName of roles) { const targetAddress = - contractName === CORE_CONTRACTS.FastSwitchboard && + (contractName === CORE_CONTRACTS.FastSwitchboard || + contractName === CORE_CONTRACTS.OpInteropSwitchboard) && roleName === ROLES.WATCHER_ROLE ? watcher : signer.address; diff --git a/hardhat-scripts/deploy/3.upgradeManagers.ts b/hardhat-scripts/deploy/3.upgradeManagers.ts index eaf3592f..38d57486 100644 --- a/hardhat-scripts/deploy/3.upgradeManagers.ts +++ b/hardhat-scripts/deploy/3.upgradeManagers.ts @@ -7,7 +7,7 @@ import { import { config as dotenvConfig } from "dotenv"; dotenvConfig(); -import { Wallet } from "ethers"; +import { constants, Wallet } from "ethers"; import { ethers } from "hardhat"; import { chains, EVMX_CHAIN_ID, mode } from "../config"; import { @@ -19,6 +19,7 @@ import { getAddresses, getInstance, getProviderFromChainSlug, + overrides, storeAddresses, } from "../utils"; @@ -48,12 +49,48 @@ export const main = async () => { await registerSb( chainAddresses[CORE_CONTRACTS.FastSwitchboard], + chain, signer, socketContract ); - await setOnchainContracts(chain, addresses); + const remoteChain = + chain.toString() == "420120000" + ? (420120001 as ChainSlug) + : (420120000 as ChainSlug); + + await addRemoteAddress( + chainAddresses[CORE_CONTRACTS.FastSwitchboard], + addresses[remoteChain]?.[CORE_CONTRACTS.FastSwitchboard], + chain, + signer, + socketContract + ); + await setSwitchboard( + chain, + addresses, + "FAST", + CORE_CONTRACTS.FastSwitchboard + ); + + if (chainAddresses[CORE_CONTRACTS.OpInteropSwitchboard]) { + await setSwitchboard( + chain, + addresses, + "OP_INTEROP", + CORE_CONTRACTS.OpInteropSwitchboard + ); + + await registerSb( + chainAddresses[CORE_CONTRACTS.OpInteropSwitchboard], + chain, + signer, + socketContract + ); + } + + await setOnchainContracts(chain, addresses); await storeAddresses(chainAddresses, chain, mode); } } catch (error) { @@ -75,38 +112,31 @@ async function setOnchainContracts(chain, addresses) { ) ).connect(signer); - const fastSBtype = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("FAST")); - const sbAddress = addresses[chain][CORE_CONTRACTS.FastSwitchboard]; const socketAddress = addresses[chain][CORE_CONTRACTS.Socket]; const contractFactoryPlugAddress = addresses[chain][CORE_CONTRACTS.ContractFactoryPlug]; const feesPlugAddress = addresses[chain][CORE_CONTRACTS.FeesPlug]; - - const currentSbAddress = await watcherPrecompile.switchboards( - chain, - fastSBtype - ); const currentSocket = await watcherPrecompile.sockets(chain); const currentContractFactoryPlug = await watcherPrecompile.contractFactoryPlug(chain); const currentFeesPlug = await watcherPrecompile.feesPlug(chain); if ( - currentSbAddress.toLowerCase() !== sbAddress.toLowerCase() || currentSocket.toLowerCase() !== socketAddress.toLowerCase() || currentContractFactoryPlug.toLowerCase() !== - contractFactoryPlugAddress.toLowerCase() || + contractFactoryPlugAddress.toLowerCase() || currentFeesPlug.toLowerCase() !== feesPlugAddress.toLowerCase() ) { const tx = await watcherPrecompile .connect(signer) .setOnChainContracts( chain, - fastSBtype, - sbAddress, socketAddress, contractFactoryPlugAddress, - feesPlugAddress + feesPlugAddress, + { + ...await overrides(chain), + } ); console.log(`Setting onchain contracts for ${chain}, txHash: `, tx.hash); @@ -114,7 +144,40 @@ async function setOnchainContracts(chain, addresses) { } } -const registerSb = async (sbAddress, signer, socket) => { +async function setSwitchboard(chain, addresses, sbType, contractName) { + const providerInstance = getProviderFromChainSlug(EVMX_CHAIN_ID as ChainSlug); + const signer: Wallet = new ethers.Wallet( + process.env.WATCHER_PRIVATE_KEY as string, + providerInstance + ); + const EVMxAddresses = addresses[EVMX_CHAIN_ID]!; + const watcherPrecompile = ( + await getInstance( + EVMxCoreContracts.WatcherPrecompile, + EVMxAddresses[EVMxCoreContracts.WatcherPrecompile] + ) + ).connect(signer); + + const sbTypeHash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(sbType)); + const sbAddress = addresses[chain][contractName]; + const currentSbAddress = await watcherPrecompile.switchboards( + chain, + sbTypeHash + ); + + if (currentSbAddress.toLowerCase() !== sbAddress.toLowerCase()) { + const tx = await watcherPrecompile + .connect(signer) + .setSwitchboard(chain, sbTypeHash, sbAddress, { + ...await overrides(chain), + }); + + console.log(`Setting switchboard for ${chain}, txHash: `, tx.hash); + await tx.wait(); + } +} + +const registerSb = async (sbAddress, chain, signer, socket) => { try { // used fast switchboard here as all have same function signature const switchboard = ( @@ -129,7 +192,9 @@ const registerSb = async (sbAddress, signer, socket) => { }); if (Number(sb) == 0) { - const registerTx = await switchboard.registerSwitchboard(); + const registerTx = await switchboard.registerSwitchboard({ + ...await overrides(chain), + }); console.log(`Registering Switchboard ${sbAddress}: ${registerTx.hash}`); await registerTx.wait(); } @@ -138,6 +203,42 @@ const registerSb = async (sbAddress, signer, socket) => { } }; +const addRemoteAddress = async ( + sbAddress, + remoteAddress, + chain, + signer, + socket +) => { + try { + // used fast switchboard here as all have same function signature + const switchboard = ( + await getInstance(CORE_CONTRACTS.OpInteropSwitchboard, sbAddress) + ).connect(signer); + + // send overrides while reading capacitor to avoid errors on mantle chain + // some chains give balance error if gas price is used with from address as zero + // therefore override from address as well + const remoteAddressContract = await switchboard.remoteAddress(); + if (remoteAddressContract == constants.AddressZero) { + console.log( + `Adding remote address ${remoteAddress} to Switchboard ${sbAddress} on ${chain}` + ); + const registerTx = await switchboard.setRemoteAddress( + remoteAddress, + { + ...await overrides(chain), + } + ); + + console.log(`Tx: ${registerTx.hash}`); + await registerTx.wait(); + } + } catch (error) { + throw error; + } +}; + main() .then(() => process.exit(0)) .catch((error: Error) => { diff --git a/hardhat-scripts/deploy/4.connect.ts b/hardhat-scripts/deploy/4.connect.ts index 0de5a503..4c58df71 100644 --- a/hardhat-scripts/deploy/4.connect.ts +++ b/hardhat-scripts/deploy/4.connect.ts @@ -97,7 +97,10 @@ async function connectPlug( const tx = await plug.functions["connectSocket"]( appGateway, socket.address, - switchboard + switchboard, + { + ...await overrides(chain), + } ); console.log( `Connecting ${plugContract} on ${chain} to ${appGateway} tx hash: ${tx.hash}` @@ -210,7 +213,7 @@ export const updateConfigEVMx = async () => { ); const { nonce, signature } = await signWatcherMessage(encodedMessage); const tx = await watcher.setAppGateways(appConfigs, nonce, signature, { - ...overrides(EVMX_CHAIN_ID), + ...await overrides(EVMX_CHAIN_ID), }); console.log(`Updating EVMx Config tx hash: ${tx.hash}`); await tx.wait(); diff --git a/hardhat-scripts/utils/overrides.ts b/hardhat-scripts/utils/overrides.ts index 7d9ccc96..8bbdf786 100644 --- a/hardhat-scripts/utils/overrides.ts +++ b/hardhat-scripts/utils/overrides.ts @@ -32,6 +32,16 @@ export const chainOverrides: { // gasLimit: 1_000_000_000, gasPrice: 0, }, + [ChainSlug.INTEROP_ALPHA_0]: { + // type: 0, + gasLimit: 1_000_000, + // gasPrice: 0, + }, + [ChainSlug.INTEROP_ALPHA_1]: { + // type: 0, + gasLimit: 1_000_000, + // gasPrice: 0, + }, }; export const overrides = async ( diff --git a/hardhat.config.ts b/hardhat.config.ts index 72ce85e8..b1ec305e 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -24,7 +24,6 @@ import { hardhatChainNameToSlug, } from "@socket.tech/socket-protocol-common"; import { EVMX_CHAIN_ID } from "./hardhat-scripts/config/config"; -import { BASE_SEPOLIA_CHAIN_ID } from "./hardhat-scripts/constants"; const dotenvConfigPath: string = process.env.DOTENV_CONFIG_PATH || "./.env"; dotenvConfig({ path: resolve(__dirname, dotenvConfigPath) }); @@ -66,9 +65,19 @@ let liveNetworks = { }, ["base_sepolia"]: { accounts: [`0x${privateKey}`], - chainId: BASE_SEPOLIA_CHAIN_ID, + chainId: ChainId.BASE_SEPOLIA, url: process.env.BASE_SEPOLIA_RPC, }, + ["interop_alpha_0"]: { + accounts: [`0x${privateKey}`], + chainId: ChainId.INTEROP_ALPHA_0, + url: process.env.INTEROP_ALPHA_0_RPC, + }, + ["interop_alpha_1"]: { + accounts: [`0x${privateKey}`], + chainId: ChainId.INTEROP_ALPHA_1, + url: process.env.INTEROP_ALPHA_1_RPC, + }, }; const config: HardhatUserConfig = { @@ -121,12 +130,28 @@ const config: HardhatUserConfig = { }, { network: "baseTestnet", - chainId: BASE_SEPOLIA_CHAIN_ID, + chainId: ChainId.BASE_SEPOLIA, urls: { apiURL: "https://api-sepolia.basescan.org/api", browserURL: "https://sepolia.basescan.org/", }, }, + { + network: "interopAlpha0", + chainId: ChainId.INTEROP_ALPHA_0, + urls: { + apiURL: "https://optimism-interop-alpha-0.blockscout.com/api", + browserURL: "https://optimism-interop-alpha-0.blockscout.com/", + }, + }, + { + network: "interopAlpha1", + chainId: ChainId.INTEROP_ALPHA_1, + urls: { + apiURL: "https://optimism-interop-alpha-1.blockscout.com/api", + browserURL: "https://optimism-interop-alpha-1.blockscout.com/", + }, + }, { network: "evmx", chainId: EVMX_CHAIN_ID, diff --git a/lib/optimism b/lib/optimism new file mode 160000 index 00000000..bc6d11a8 --- /dev/null +++ b/lib/optimism @@ -0,0 +1 @@ +Subproject commit bc6d11a854b4d4cd60d9de541eb60673912d4f76 diff --git a/package.json b/package.json index aee7ad7e..b142610b 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@aws-sdk/client-s3": "^3.670.0", "@nomicfoundation/hardhat-verify": "^2.0.12", "@nomiclabs/hardhat-ethers": "2.2.3", - "@socket.tech/socket-protocol-common": "1.1.31", + "@socket.tech/socket-protocol-common": "1.1.32", "@typechain/ethers-v5": "^10.0.0", "@typechain/hardhat": "6.0.0", "dotenv": "^16.0.3", diff --git a/remappings.txt b/remappings.txt index 1cf3bb7d..4688d27f 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,4 +1,5 @@ ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ solmate/=lib/solmate/src/ -solady/=lib/solady/src/ \ No newline at end of file +solady/=lib/solady/src/ +optimism/=lib/optimism/packages/contracts-bedrock/ \ No newline at end of file diff --git a/script/helpers/AppGatewayFeeBalance.s.sol b/script/helpers/AppGatewayFeeBalance.s.sol index 6637a83b..c87304fa 100644 --- a/script/helpers/AppGatewayFeeBalance.s.sol +++ b/script/helpers/AppGatewayFeeBalance.s.sol @@ -13,16 +13,18 @@ contract CheckDepositedFees is Script { FeesManager feesManager = FeesManager(payable(vm.envAddress("FEES_MANAGER"))); address appGateway = vm.envAddress("APP_GATEWAY"); + uint32 chainSlug = 420120000; + (uint256 deposited, uint256 blocked) = feesManager.appGatewayFeeBalances( appGateway, - 421614, + chainSlug, ETH_ADDRESS ); console.log("App Gateway:", appGateway); console.log("Deposited fees:", deposited); console.log("Blocked fees:", blocked); - uint256 availableFees = feesManager.getAvailableFees(421614, appGateway, ETH_ADDRESS); + uint256 availableFees = feesManager.getAvailableFees(chainSlug, appGateway, ETH_ADDRESS); console.log("Available fees:", availableFees); } } diff --git a/script/helpers/PayFeesInArbitrumETH.s.sol b/script/helpers/PayFeesInArbitrumETH.s.sol index 50da4fc1..a658e356 100644 --- a/script/helpers/PayFeesInArbitrumETH.s.sol +++ b/script/helpers/PayFeesInArbitrumETH.s.sol @@ -9,11 +9,11 @@ import {ETH_ADDRESS} from "../../contracts/protocol/utils/common/Constants.sol"; contract DepositFees is Script { function run() external { - vm.createSelectFork(vm.envString("ARBITRUM_SEPOLIA_RPC")); - - uint256 privateKey = vm.envUint("PRIVATE_KEY"); + vm.createSelectFork(vm.envString("INTEROP_ALPHA_0_RPC")); + uint256 privateKey = vm.envUint("SOCKET_SIGNER_KEY"); vm.startBroadcast(privateKey); - FeesPlug feesPlug = FeesPlug(payable(vm.envAddress("ARBITRUM_FEES_PLUG"))); + + FeesPlug feesPlug = FeesPlug(payable(vm.envAddress("FEES_PLUG"))); address appGateway = vm.envAddress("APP_GATEWAY"); address sender = vm.addr(privateKey); @@ -21,7 +21,7 @@ contract DepositFees is Script { uint256 balance = sender.balance; console.log("Sender balance in wei:", balance); - uint feesAmount = 0.001 ether; + uint feesAmount = 0.0001 ether; feesPlug.deposit{value: feesAmount}(ETH_ADDRESS, appGateway, feesAmount); } } diff --git a/script/helpers/WithdrawFeesArbitrumFeesPlug.s.sol b/script/helpers/WithdrawFeesArbitrumFeesPlug.s.sol new file mode 100644 index 00000000..fc954d26 --- /dev/null +++ b/script/helpers/WithdrawFeesArbitrumFeesPlug.s.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {FeesManager} from "../../contracts/protocol/payload-delivery/FeesManager.sol"; +import {ETH_ADDRESS} from "../../contracts/protocol/utils/common/Constants.sol"; +import {CounterAppGateway} from "../../test/apps/app-gateways/counter/CounterAppGateway.sol"; + +// @notice This script is used to withdraw fees from EVMX to Arbitrum Sepolia +// @dev Make sure your app has withdrawFeeTokens() function implemented. You can check its implementation in CounterAppGateway.sol +contract WithdrawFees is Script { + function run() external { + // EVMX Check available fees + vm.createSelectFork(vm.envString("EVMX_RPC")); + FeesManager feesManager = FeesManager(payable(vm.envAddress("FEES_MANAGER"))); + address appGatewayAddress = vm.envAddress("APP_GATEWAY"); + + CounterAppGateway appGateway = CounterAppGateway(appGatewayAddress); + uint256 availableFees = feesManager.getAvailableFees( + 421614, + appGatewayAddress, + ETH_ADDRESS + ); + console.log("Available fees:", availableFees); + + if (availableFees > 0) { + // Switch to Arbitrum Sepolia to get gas price + vm.createSelectFork(vm.envString("ARBITRUM_SEPOLIA_RPC")); + uint256 privateKey = vm.envUint("PRIVATE_KEY"); + address sender = vm.addr(privateKey); + + // Gas price from Arbitrum + uint256 arbitrumGasPrice = block.basefee + 0.1 gwei; // With buffer + uint256 gasLimit = 5_000_000; // Estimate + uint256 estimatedGasCost = gasLimit * arbitrumGasPrice; + + console.log("Arbitrum gas price (wei):", arbitrumGasPrice); + console.log("Gas limit:", gasLimit); + console.log("Estimated gas cost:", estimatedGasCost); + + // Calculate amount to withdraw + uint256 amountToWithdraw = availableFees > estimatedGasCost + ? availableFees - estimatedGasCost + : 0; + + if (amountToWithdraw > 0) { + // Switch back to EVMX to perform withdrawal + vm.createSelectFork(vm.envString("EVMX_RPC")); + vm.startBroadcast(privateKey); + console.log("Withdrawing amount:", amountToWithdraw); + appGateway.withdrawFeeTokens(421614, ETH_ADDRESS, amountToWithdraw, sender); + vm.stopBroadcast(); + + // Switch back to Arbitrum Sepolia to check final balance + vm.createSelectFork(vm.envString("ARBITRUM_SEPOLIA_RPC")); + console.log("Final sender balance:", sender.balance); + } else { + console.log("Available fees less than estimated gas cost"); + } + } + } +} diff --git a/script/super-token/Bridge.s.sol b/script/super-token/Bridge.s.sol new file mode 100644 index 00000000..8e03bd04 --- /dev/null +++ b/script/super-token/Bridge.s.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {Fees} from "../../contracts/protocol/utils/common/Structs.sol"; +import {ETH_ADDRESS} from "../../contracts/protocol/utils/common/Constants.sol"; +import {SuperTokenAppGateway} from "../../test/apps/app-gateways/super-token-op/SuperTokenAppGateway.sol"; + +contract Bridge is Script { + function run() external { + vm.createSelectFork(vm.envString("EVMX_RPC")); + uint256 privateKey = vm.envUint("SOCKET_SIGNER_KEY"); + vm.startBroadcast(privateKey); + + SuperTokenAppGateway.TransferOrder memory order = SuperTokenAppGateway.TransferOrder({ + srcToken: 0x9C79440aD7e70b895d88433D7B268BA4482E406F, + dstToken: 0xD6CE61B9BE8C8aD07b043e61079d66FB10f2E405, + user: 0xb62505feacC486e809392c65614Ce4d7b051923b, + amount: 100000, + deadline: block.timestamp + 1 days + }); + + SuperTokenAppGateway gateway = SuperTokenAppGateway(vm.envAddress("APP_GATEWAY")); + bytes memory payload = abi.encode(order); + console.logBytes(payload); + gateway.transfer(payload); + } +} diff --git a/script/super-token/DeployGateway.s.sol b/script/super-token/DeployGateway.s.sol new file mode 100644 index 00000000..caa77795 --- /dev/null +++ b/script/super-token/DeployGateway.s.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {SuperTokenAppGateway} from "../../test/apps/app-gateways/super-token-op/SuperTokenAppGateway.sol"; +import {Fees} from "../../contracts/protocol/utils/common/Structs.sol"; +import {ETH_ADDRESS} from "../../contracts/protocol/utils/common/Constants.sol"; + +contract DeployTokenGateway 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"); + address deployer = vm.addr(deployerPrivateKey); + vm.startBroadcast(deployerPrivateKey); + + // Setting fee payment on Arbitrum Sepolia + Fees memory fees = Fees({ + feePoolChain: 420120000, + feePoolToken: ETH_ADDRESS, + amount: 0.00001 ether + }); + + SuperTokenAppGateway gateway = new SuperTokenAppGateway(addressResolver, deployer, fees); + + console.log("Contracts deployed:"); + console.log("SuperTokenAppGateway:", address(gateway)); + console.log("superTokenId:"); + console.logBytes32(gateway.superToken()); + } +} diff --git a/script/super-token/DeployToken.s.sol b/script/super-token/DeployToken.s.sol new file mode 100644 index 00000000..4f764b16 --- /dev/null +++ b/script/super-token/DeployToken.s.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {SuperTokenAppGateway} from "../../test/apps/app-gateways/super-token-op/SuperTokenAppGateway.sol"; + +contract SuperTokenDeploy is Script { + function run() external { + string memory rpc = vm.envString("EVMX_RPC"); + address appGatewayAddress = vm.envAddress("APP_GATEWAY"); + vm.createSelectFork(rpc); + + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + // Setting fee payment on Arbitrum Sepolia + SuperTokenAppGateway gateway = SuperTokenAppGateway(appGatewayAddress); + + gateway.deployContracts(420120000); + gateway.deployContracts(420120001); + + console.log("Tokens deployed"); + } +} diff --git a/script/super-token/GetToken.s.sol b/script/super-token/GetToken.s.sol new file mode 100644 index 00000000..3540bd25 --- /dev/null +++ b/script/super-token/GetToken.s.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {SuperTokenAppGateway} from "../../test/apps/app-gateways/super-token-op/SuperTokenAppGateway.sol"; +import {Fees} from "../../contracts/protocol/utils/common/Structs.sol"; +import {ETH_ADDRESS} from "../../contracts/protocol/utils/common/Constants.sol"; + +contract GetToken is Script { + function run() external { + string memory rpc = vm.envString("EVMX_RPC"); + vm.createSelectFork(rpc); + // Setting fee payment on Arbitrum Sepolia + SuperTokenAppGateway gateway = SuperTokenAppGateway(vm.envAddress("APP_GATEWAY")); + bytes32 superTokenId = gateway.superToken(); + + address op0Token = gateway.getOnChainAddress(superTokenId, 420120000); + address op1Token = gateway.getOnChainAddress(superTokenId, 420120001); + + address op0TokenForwarder = gateway.forwarderAddresses(superTokenId, 420120000); + address op1TokenForwarder = gateway.forwarderAddresses(superTokenId, 420120001); + + console.log("Contracts deployed:"); + console.log("Op 0 address:", op0Token); + console.log("Op 1 address:", op1Token); + console.log("Op 0 forwarder:", op0TokenForwarder); + console.log("Op 1 forwarder:", op1TokenForwarder); + } +} diff --git a/script/super-token/SetToken.s.sol b/script/super-token/SetToken.s.sol new file mode 100644 index 00000000..a27af9e6 --- /dev/null +++ b/script/super-token/SetToken.s.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {Fees} from "../../contracts/protocol/utils/common/Structs.sol"; +import {ETH_ADDRESS} from "../../contracts/protocol/utils/common/Constants.sol"; +import {OpInteropSwitchboard} from "../../contracts/protocol/socket/switchboard/OpInteropSwitchboard.sol"; + +contract SetToken is Script { + function run() external { + vm.createSelectFork(vm.envString("INTEROP_ALPHA_0_RPC")); + uint256 privateKey = vm.envUint("SOCKET_SIGNER_KEY"); + vm.startBroadcast(privateKey); + + OpInteropSwitchboard switchboard = OpInteropSwitchboard( + 0x9EDfb162b725CF6d628D68af200cAe8b624111eD + ); + // Op 0 address: 0x46fc6C778E8F69fB97538530D1f4eCe674719604 + // Op 1 address: 0x897f6507bFE6C365394377b86C158Df05e3DD12b + switchboard.setToken(0x46fc6C778E8F69fB97538530D1f4eCe674719604); + } +} diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index fa419020..0655fe14 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -9,6 +9,7 @@ import "../contracts/interfaces/IForwarder.sol"; import "../contracts/protocol/utils/common/AccessRoles.sol"; import {Socket} from "../contracts/protocol/socket/Socket.sol"; import "../contracts/protocol/socket/switchboard/FastSwitchboard.sol"; +import "../contracts/protocol/socket/switchboard/OpInteropSwitchboard.sol"; import "../contracts/protocol/socket/SocketBatcher.sol"; import "../contracts/protocol/AddressResolver.sol"; import {ContractFactoryPlug} from "../contracts/protocol/payload-delivery/ContractFactoryPlug.sol"; @@ -30,8 +31,9 @@ contract SetupTest is Test { address watcherEOA = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; address transmitterEOA = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8; - uint32 arbChainSlug = 421614; - uint32 optChainSlug = 11155420; + uint32 arbChainSlug = 420120000; + uint32 optChainSlug = 420120001; + uint32 evmxSlug = 1; uint256 expiryTime = 10000000; @@ -45,7 +47,7 @@ contract SetupTest is Test { struct SocketContracts { uint32 chainSlug; Socket socket; - FastSwitchboard switchboard; + OpInteropSwitchboard switchboard; SocketBatcher socketBatcher; ContractFactoryPlug contractFactoryPlug; FeesPlug feesPlug; @@ -66,7 +68,7 @@ contract SetupTest is Test { function deploySocket(uint32 chainSlug_) internal returns (SocketContracts memory) { Socket socket = new Socket(chainSlug_, owner, "test"); SocketBatcher socketBatcher = new SocketBatcher(owner, socket); - FastSwitchboard switchboard = new FastSwitchboard(chainSlug_, socket, owner); + OpInteropSwitchboard switchboard = new OpInteropSwitchboard(chainSlug_, socket, owner); FeesPlug feesPlug = new FeesPlug(address(socket), owner); ContractFactoryPlug contractFactoryPlug = new ContractFactoryPlug(address(socket), owner); @@ -80,16 +82,17 @@ contract SetupTest is Test { switchboard.grantWatcherRole(watcherEOA); vm.stopPrank(); - hoax(watcherEOA); + vm.startPrank(watcherEOA); watcherPrecompile.setOnChainContracts( chainSlug_, - FAST, - address(switchboard), address(socket), address(contractFactoryPlug), address(feesPlug) ); + watcherPrecompile.setSwitchboard(chainSlug_, keccak256("FAST"), address(switchboard)); + vm.stopPrank(); + return SocketContracts({ chainSlug: chainSlug_, diff --git a/test/apps/SuperToken.t.sol b/test/apps/SuperToken.t.sol index 34b438ac..a8284537 100644 --- a/test/apps/SuperToken.t.sol +++ b/test/apps/SuperToken.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {SuperTokenAppGateway} from "./app-gateways/super-token/SuperTokenAppGateway.sol"; -import {SuperToken} from "./app-gateways/super-token/SuperToken.sol"; +import {SuperTokenAppGateway} from "./app-gateways/super-token-op/SuperTokenAppGateway.sol"; +import {SuperToken} from "./app-gateways/super-token-op/SuperToken.sol"; import "../DeliveryHelper.t.sol"; import {QUERY, FINALIZE, SCHEDULE} from "../../contracts/protocol/utils/common/Constants.sol"; @@ -36,7 +36,7 @@ contract SuperTokenTest is DeliveryHelperTest { bytes32[] contractIds = new bytes32[](1); /// @dev Test amount for token transfers (0.01 ETH) - uint256 srcAmount = 0.01 ether; + uint256 amount = 0.01 ether; /// @dev Structure holding transfer order details SuperTokenAppGateway.TransferOrder transferOrder; @@ -65,14 +65,7 @@ contract SuperTokenTest is DeliveryHelperTest { SuperTokenAppGateway superTokenApp = new SuperTokenAppGateway( address(addressResolver), owner, - createFees(maxFees), - SuperTokenAppGateway.ConstructorParams({ - name_: "SUPER TOKEN", - symbol_: "SUPER", - decimals_: 18, - initialSupplyHolder_: owner, - initialSupply_: 1000000000 ether - }) + createFees(maxFees) ); // 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 @@ -153,11 +146,15 @@ contract SuperTokenTest is DeliveryHelperTest { uint256 arbBalanceBefore = SuperToken(onChainArb).balanceOf(owner); uint256 optBalanceBefore = SuperToken(onChainOpt).balanceOf(owner); + hoax(owner); + getSocketConfig(optChainSlug).switchboard.setToken(onChainOpt); + getSocketConfig(optChainSlug).switchboard.syncIn(owner, amount); + transferOrder = SuperTokenAppGateway.TransferOrder({ srcToken: forwarderArb, dstToken: forwarderOpt, user: owner, - srcAmount: srcAmount, + amount: amount, deadline: block.timestamp + 1000000 }); bytes memory encodedOrder = abi.encode(transferOrder); @@ -172,13 +169,13 @@ contract SuperTokenTest is DeliveryHelperTest { assertEq( SuperToken(onChainArb).balanceOf(owner), - arbBalanceBefore - srcAmount, - "Arb balance should be decreased by srcAmount" + arbBalanceBefore - amount, + "Arb balance should be decreased by amount" ); assertEq( SuperToken(onChainOpt).balanceOf(owner), - optBalanceBefore + srcAmount, - "Opt balance should be increased by srcAmount" + optBalanceBefore + amount, + "Opt balance should be increased by amount" ); } } diff --git a/test/apps/app-gateways/super-token-op/ISuperToken.sol b/test/apps/app-gateways/super-token-op/ISuperToken.sol new file mode 100644 index 00000000..77dbb353 --- /dev/null +++ b/test/apps/app-gateways/super-token-op/ISuperToken.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +interface ISuperToken { + function burn(address user_, uint256 amount_) external; + + function mint(address receiver_, uint256 amount_) external; + + function lockTokens(address user_, uint256 amount_) external; + + function unlockTokens(address user_, uint256 amount_) external; + + // Public variable + function controller() external returns (address); + + function setController(address controller_) external; + + function balanceOf(address account_) external; +} diff --git a/test/apps/app-gateways/super-token-op/SuperToken.sol b/test/apps/app-gateways/super-token-op/SuperToken.sol new file mode 100644 index 00000000..6b52232e --- /dev/null +++ b/test/apps/app-gateways/super-token-op/SuperToken.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +import "solmate/tokens/ERC20.sol"; +import {Ownable} from "solady/auth/Ownable.sol"; +import "../../../../contracts/base/PlugBase.sol"; + +interface ISwitchboard { + function checkAndConsume(address user_, uint256 amount_) external; +} + +/** + * @title SuperToken + * @notice An ERC20 contract which enables bridging a token to its sibling chains. + */ +contract SuperToken is ERC20, Ownable, PlugBase { + address public opSwitchboard; + error InvalidSender(); + + constructor( + string memory name_, + string memory symbol_, + uint8 decimals_, + address initialSupplyHolder_, + uint256 initialSupply_ + ) ERC20(name_, symbol_, decimals_) { + _mint(initialSupplyHolder_, initialSupply_); + } + + function mint(address user_, uint256 amount_) external onlySocket { + ISwitchboard(opSwitchboard).checkAndConsume(user_, amount_); + _mint(user_, amount_); + } + + function burn(address user_, uint256 amount_) external onlySocket { + _burn(user_, amount_); + } + + //// INITIALIZATION //// + + function setupToken(address owner_, address opSwitchboard_) external { + if (owner() != address(0) && owner() != msg.sender) revert InvalidSender(); + _initializeOwner(owner_); + opSwitchboard = opSwitchboard_; + } +} diff --git a/test/apps/app-gateways/super-token-op/SuperTokenAppGateway.sol b/test/apps/app-gateways/super-token-op/SuperTokenAppGateway.sol new file mode 100644 index 00000000..fde2c8d5 --- /dev/null +++ b/test/apps/app-gateways/super-token-op/SuperTokenAppGateway.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.21; + +import "solady/auth/Ownable.sol"; + +import "../../../../contracts/base/AppGatewayBase.sol"; +import "./ISuperToken.sol"; +import "./SuperToken.sol"; + +contract SuperTokenAppGateway is AppGatewayBase, Ownable { + bytes32 public superToken = _createContractId("superToken"); + string public status; + + event Transferred(bytes32 asyncId); + + struct TransferOrder { + address srcToken; + address dstToken; + address user; + uint256 amount; + uint256 deadline; + } + + constructor( + address addressResolver_, + address owner_, + Fees memory fees_ + ) AppGatewayBase(addressResolver_) { + creationCodeWithArgs[superToken] = abi.encodePacked( + type(SuperToken).creationCode, + abi.encode("SUPER TOKEN", "SUPER", 18, owner_, 1000000000 ether) + ); + + // sets the fees data like max fees, chain and token for all transfers + // they can be updated for each transfer as well + _setOverrides(fees_); + _initializeOwner(owner_); + } + + function deployContracts(uint32 chainSlug_) external async { + address opInteropSwitchboard = watcherPrecompile__().switchboards(chainSlug_, sbType); + bytes memory initData = abi.encodeWithSelector( + SuperToken.setupToken.selector, + owner(), + opInteropSwitchboard + ); + _deploy(superToken, chainSlug_, IsPlug.YES, initData); + } + + function transfer(bytes memory order_) external async { + TransferOrder memory order = abi.decode(order_, (TransferOrder)); + status = "Bridging"; + + ISuperToken(order.srcToken).burn(order.user, order.amount); + ISuperToken(order.dstToken).mint(order.user, order.amount); + IPromise(order.dstToken).then(this.updateStatus.selector, abi.encode("Bridged")); + + emit Transferred(_getCurrentAsyncId()); + } + + function updateStatus(string memory status_, bytes memory) external onlyPromises { + status = status_; + } +} diff --git a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol index 08fd2eac..5da8a06b 100644 --- a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol +++ b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol @@ -23,7 +23,7 @@ contract SuperTokenAppGateway is AppGatewayBase, Ownable { address srcToken; address dstToken; address user; - uint256 srcAmount; + uint256 amount; uint256 deadline; } @@ -63,8 +63,8 @@ contract SuperTokenAppGateway is AppGatewayBase, Ownable { function transfer(bytes memory order_) external async { TransferOrder memory order = abi.decode(order_, (TransferOrder)); - ISuperToken(order.srcToken).burn(order.user, order.srcAmount); - ISuperToken(order.dstToken).mint(order.user, order.srcAmount); + ISuperToken(order.srcToken).burn(order.user, order.amount); + ISuperToken(order.dstToken).mint(order.user, order.amount); emit Transferred(_getCurrentAsyncId()); } diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index 98c87f09..17038fbf 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -68,16 +68,10 @@ contract MockSocket is ISocket { uint64 public callCounter; uint32 public chainSlug; - enum ExecutionStatus { - NotExecuted, - Executed, - Reverted - } - /** * @dev keeps track of whether a payload has been executed or not using payload id */ - mapping(bytes32 => ExecutionStatus) public payloadExecuted; + mapping(bytes32 => ExecutionStatus) public override payloadExecuted; constructor(uint32 chainSlug_, address, address, address, string memory) { chainSlug = chainSlug_; diff --git a/testScript.sh b/testScript.sh index c3a1f30c..e0bbbe5c 100644 --- a/testScript.sh +++ b/testScript.sh @@ -55,7 +55,27 @@ source .env && forge script script/mock/FinalizeAndExecution.s.sol --broadcast - source .env && forge script script/admin/checkLimits.s.sol --broadcast --skip-simulation source .env && forge script script/admin/UpdateLimits.s.sol --broadcast --skip-simulation +source .env && forge script script/super-token/Bridge.s.sol --broadcast --skip-simulation --private-key $PRIVATE_KEY --legacy --with-gas-price 0 # add fees source .env && forge script script/helpers/PayFeesInArbitrumETH.s.sol --broadcast --skip-simulation source .env && forge script script/helpers/AppGatewayFeeBalance.s.sol + + + +source .env && forge script script/super-token/DeployGateway.s.sol --broadcast --skip-simulation --private-key $PRIVATE_KEY --legacy --with-gas-price 0 +source .env && forge script script/helpers/PayFeesInArbitrumETH.s.sol --broadcast --skip-simulation --private-key $PRIVATE_KEY + +source .env && cast send $APP_GATEWAY "deployContracts(uint32)" 420120000 --private-key $PRIVATE_KEY --legacy --gas-price 0 +source .env && cast send $APP_GATEWAY "deployContracts(uint32)" 420120001 --private-key $PRIVATE_KEY --legacy --gas-price 0 + +source .env && forge script script/super-token/GetToken.s.sol --broadcast --skip-simulation +source .env && forge script script/super-token/SetToken.s.sol --broadcast --skip-simulation --private-key $PRIVATE_KEY + + + + +# // commands +source .env && cast call $APP_GATEWAY "status()" --rpc-url $EVMX_RPC | cast to-ascii + +source .env && cast send $APP_GATEWAY "transfer(bytes)" 0x0000000000000000000000009c79440ad7e70b895d88433d7b268ba4482e406f000000000000000000000000d6ce61b9be8c8ad07b043e61079d66fb10f2e405000000000000000000000000b62505feacc486e809392c65614ce4d7b051923b00000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000067c88d59 --private-key $PRIVATE_KEY --legacy --gas-price 0 diff --git a/yarn.lock b/yarn.lock index 3cf308a7..6178a636 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2071,10 +2071,10 @@ "@smithy/types" "^4.1.0" tslib "^2.6.2" -"@socket.tech/socket-protocol-common@1.1.31": - version "1.1.31" - resolved "https://registry.yarnpkg.com/@socket.tech/socket-protocol-common/-/socket-protocol-common-1.1.31.tgz#d95b1b6caf69a11c6bd13b18b7871c57e7d283fb" - integrity sha512-Hp1er4kRqCfeuvQFFgxooT0XBbO4nBrpL8/SYB/BNMdWfR9hk6PVE8MUnoIqTUJJTvIePKk+GOajl8MVqPD3VQ== +"@socket.tech/socket-protocol-common@1.1.32": + version "1.1.32" + resolved "https://registry.yarnpkg.com/@socket.tech/socket-protocol-common/-/socket-protocol-common-1.1.32.tgz#02779e83bed9b3cf51a9cdce655f6522aaa20ad4" + integrity sha512-1BUeqFbDrZBn56mBzSivtWWMMMhUqZzGuwmKEu6ZKjJfZhZInVYNs3ZUkhVaZSznpMZSYt6qypWkQHj3nTQZIA== dependencies: "@socket.tech/socket-protocol" "^1.0.15" axios "^1.7.9"