diff --git a/Errors.md b/Errors.md index 93712470..506cf6d3 100644 --- a/Errors.md +++ b/Errors.md @@ -13,6 +13,7 @@ | `PromiseAlreadyResolved()` | `0x56b63537` | | `OnlyForwarderOrLocalInvoker()` | `0xa9fb0b28` | | `PromiseAlreadySetUp()` | `0x927c53d5` | +| `PromiseRevertFailed()` | `0x0175b9de` | ## apps/payload-delivery/ContractFactoryPlug.sol @@ -47,6 +48,8 @@ | `CallFailed(bytes32)` | `0xe22e3683` | | `PayloadTooLarge()` | `0x492f620d` | | `OnlyAppGateway()` | `0xfec944ea` | +| `WinningBidExists()` | `0xe8733654` | +| `InsufficientFees()` | `0x8d53e553` | ## apps/payload-delivery/app-gateway/FeesManager.sol @@ -101,11 +104,13 @@ | `InvalidIndex()` | `0x63df8171` | | `FeesNotSet()` | `0x2a831034` | -## libraries/RescueFundsLib.sol +## libraries/ECDSA.sol -| Error | Signature | -| ----------------------- | ------------ | -| `InvalidTokenAddress()` | `0x1eb00b06` | +| Error | Signature | +| -------------------------------------- | ------------ | +| `ECDSAInvalidSignature()` | `0xf645eedf` | +| `ECDSAInvalidSignatureLength(uint256)` | `0xfce698f7` | +| `ECDSAInvalidSignatureS(bytes32)` | `0xd78bce0c` | ## mock/MockSocket.sol @@ -134,7 +139,7 @@ | `InvalidSlug()` | `0x290a8315` | | `ExecutionFailed()` | `0xacfdb444` | -## socket/SocketBase.sol +## socket/SocketUtils.sol | Error | Signature | | ---------------------- | ------------ | @@ -167,6 +172,12 @@ | ------------------- | ------------ | | `NoPermit(bytes32)` | `0x962f6333` | +## socket/utils/SignatureVerifier.sol + +| Error | Signature | +| -------------------- | ------------ | +| `InvalidSigLength()` | `0xd2453293` | + ## utils/AddressResolverUtil.sol | Error | Signature | diff --git a/EventTopics.md b/EventTopics.md new file mode 100644 index 00000000..fd91d9d2 --- /dev/null +++ b/EventTopics.md @@ -0,0 +1,185 @@ +# Event Topics + +## AddressResolver.sol + +| Event | Topic | +| ----------------------- | -------------------------------------------------------------------- | +| `PlugAdded` | `0x2cb8d865028f9abf3dc064724043264907615fadc8615a3699a85edb66472273` | +| `ForwarderDeployed` | `0x4dbbecb9cf9c8b93da9743a2b48ea52efe68d69230ab1c1b711891d9d223b29f` | +| `AsyncPromiseDeployed` | `0xb6c5491cf83e09749b1a4dd6a9f07b0e925fcb0a915ac8c2b40e8ab28191c270` | +| `ImplementationUpdated` | `0xa1e41aa2c2f3f20d9b63ac06b634d2788768d6034f3d9192cdf7d07374bb16f4` | + +## apps/cron/CronAppGateway.sol + +| Event | Topic | +| ----------------- | -------------------------------------------------------------------- | +| `TimeoutResolved` | `0x21f74fe97870cde3cfbde7addf2b9343c27ca433b826801e63b3d6153699ec3f` | + +## apps/payload-delivery/ContractFactoryPlug.sol + +| Event | Topic | +| ---------- | -------------------------------------------------------------------- | +| `Deployed` | `0x94bfd9af14ef450884c8a7ddb5734e2e1e14e70a1c84f0801cc5a29e34d26428` | + +## apps/payload-delivery/FeesPlug.sol + +| Event | Topic | +| --------------- | -------------------------------------------------------------------- | +| `FeesDeposited` | `0x0fd38537e815732117cfdab41ba9b6d3eb2c5799d44039c100c05fc9c112f235` | +| `FeesWithdrawn` | `0x87044da2612407bc001bb0985725dcc651a0dc71eaabfd1d7e8617ca85a8c19c` | + +## apps/payload-delivery/app-gateway/AuctionManager.sol + +| Event | Topic | +| ---------------- | -------------------------------------------------------------------- | +| `AuctionStarted` | `0x884055d82c091219e252c5c2acca6051c823fcba97cecb3ba07d5f06c1628e6c` | +| `AuctionEnded` | `0xefa02d67c6f01c72b191e6c5076e63d9a4941f9037bf1c4aa4ccc613340e1571` | +| `BidPlaced` | `0xaf5c23b337289338f72cec2fdec2c736d419c7607e4c840ee4d28176477d4e08` | + +## apps/payload-delivery/app-gateway/BatchAsync.sol + +| Event | Topic | +| ----------------------- | -------------------------------------------------------------------- | +| `PayloadSubmitted` | `0xaab415570fdc7fb2a70601b49667178ab5f887c7901dc5e84b853c5bad514106` | +| `FeesIncreased` | `0xf1a5d6adcecf6c2be482b515e9564a9898cd629b54c57ede69295dffbf16bb1d` | +| `PayloadAsyncRequested` | `0x71c5226e31c1ad0fb69f89c62225760ba7c06c4ebdd284dfc71fd61603120d95` | +| `BatchCancelled` | `0xac01c50ce693d1fe783ba60ec0f82ab99f65e62446fffb028e3bd639cd5684d4` | + +## apps/payload-delivery/app-gateway/DeliveryHelper.sol + +| Event | Topic | +| ------------------ | -------------------------------------------------------------------- | +| `CallBackReverted` | `0xb793657774d4efcce6746ecc3d896279387c392fe6f6f2932daf35d5a1d65be6` | + +## apps/payload-delivery/app-gateway/FeesManager.sol + +| Event | Topic | +| -------------------------- | -------------------------------------------------------------------- | +| `FeesBlocked` | `0xc1639cc709c1b397fa20d3a1ece33aee2eb5c29573fe9e1e06d2364d40cdf2f7` | +| `TransmitterFeesUpdated` | `0x409067fdd2f9c474161a5b1f96399f2d24c45cfc4a93f4b1c27c9bf351f1e2bb` | +| `FeesDepositedUpdated` | `0xe82dece33ef85114446a366b7d94538d641968e3ec87bf9f2f5a957ace1086e7` | +| `FeesUnblockedAndAssigned` | `0x09a0f93a20f672833eeac3a1c9d63495cfcbee2e32f968e947a6124054b44a79` | +| `FeesUnblocked` | `0x5290de2b93b8542dc233ee9f02c346e94a47254ed50e00738e6d7ec4a54bcb3a` | + +## apps/payload-delivery/app-gateway/QueueAsync.sol + +| Event | Topic | +| ----------------------- | -------------------------------------------------------------------- | +| `PayloadBatchCancelled` | `0xae2e532ea00438ec2fc3e806f19c85f21334853d16c32107c75ff8d176af7633` | + +## apps/super-token/SuperTokenAppGateway.sol + +| Event | Topic | +| ------------- | -------------------------------------------------------------------- | +| `Transferred` | `0xd729be146db2a3467d5eaf1eed3f1afcdd5cc05d1b3b14d27675e36844fb3aba` | + +## apps/super-token-lockable/SuperTokenLockableAppGateway.sol + +| Event | Topic | +| --------- | -------------------------------------------------------------------- | +| `Bridged` | `0x19138f4129b378a375fc2f5ca3b1be3fea2f048ef2b1884a1d0fff5dca83e0ac` | + +## base/PlugBase.sol + +| Event | Topic | +| --------------------------- | -------------------------------------------------------------------- | +| `ConnectorPlugDisconnected` | `0xc2af098c82dba3c4b00be8bda596d62d13b98a87b42626fefa67e0bb0e198fdd` | + +## interfaces/IAddressResolver.sol + +| Event | Topic | +| ------------ | -------------------------------------------------------------------- | +| `AddressSet` | `0x9ef0e8c8e52743bb38b83b17d9429141d494b8041ca6d616a6c77cebae9cd8b7` | + +## interfaces/IDeliveryHelper.sol + +| Event | Topic | +| -------------- | -------------------------------------------------------------------- | +| `BidPlaced` | `0xaf5c23b337289338f72cec2fdec2c736d419c7607e4c840ee4d28176477d4e08` | +| `AuctionEnded` | `0xefa02d67c6f01c72b191e6c5076e63d9a4941f9037bf1c4aa4ccc613340e1571` | + +## interfaces/IERC20.sol + +| Event | Topic | +| ---------- | -------------------------------------------------------------------- | +| `Transfer` | `0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef` | +| `Approval` | `0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925` | + +## interfaces/ISocket.sol + +| Event | Topic | +| ------------------------- | -------------------------------------------------------------------- | +| `ExecutionSuccess` | `0xc54787fbe087097b182e713f16d3443ad2e67cbe6732628451dd3695a11814c2` | +| `PlugConnected` | `0x99c37c6da3bd69c6d59967915f8339f11a0a17fed28c615efb19457fdec0d7db` | +| `AppGatewayCallRequested` | `0x392cb36fae7bd0470268c65b15c32a745b37168c4ccd13348c59bd9170f3b3e8` | + +## mock/MockWatcherPrecompile.sol + +| Event | Topic | +| ------------------- | -------------------------------------------------------------------- | +| `CalledAppGateway` | `0x255bcf22d238fe60f6611670cd7919d2bc890283be2fdaf6d2ad3411e777e33c` | +| `QueryRequested` | `0x19a4898a6390d795c9d86362d12853f4f6515b41b95a57f9b28774b4b36fc916` | +| `FinalizeRequested` | `0x17e763b469fcd8535794b9c8c1452d90597be1eeb0eb0367662e18c067fe9656` | +| `Finalized` | `0x8ee21307ed5b839a691d488e11de81bee61525893865b7ae106cc712ef42376e` | +| `PromiseResolved` | `0x2e6de63938bd94bb20d9aa80f8bc4b07be2d8fbd4525664156351836588ed365` | +| `TimeoutRequested` | `0xdf94fed77e41734b8a17815476bbbf88e2db15d762f42a30ddb9d7870f2fb858` | +| `TimeoutResolved` | `0x221462ec065e22637f794ec3a7edb17b2f04bec88f0546dda308bc37a83801b8` | + +## socket/SocketBase.sol + +| Event | Topic | +| ---------------------- | -------------------------------------------------------------------- | +| `HasherSet` | `0xc7238b23787eb9f8aa74c79b27083bb4b8f0db527df90366b487c40fa0ba2d3a` | +| `SignatureVerifierSet` | `0xcb341def955dbf73920141be056568edde30b1dbc56a29396945e5596a03c68c` | + +## socket/SocketConfig.sol + +| Event | Topic | +| ------------------ | -------------------------------------------------------------------- | +| `SwitchboardAdded` | `0x1595852923edfbbf906f09fc8523e4cfb022a194773c4d1509446b614146ee88` | + +## socket/switchboard/FastSwitchboard.sol + +| Event | Topic | +| ---------- | -------------------------------------------------------------------- | +| `Attested` | `0x2f8e66b1207a4b70274a2a3da88ffb5737c8214576490da1b35acc38b2d62db6` | + +## socket/utils/AccessControl.sol + +| Event | Topic | +| ------------- | -------------------------------------------------------------------- | +| `RoleGranted` | `0x2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f3` | +| `RoleRevoked` | `0x155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a52` | + +## utils/OwnableTwoStep.sol + +| Event | Topic | +| ---------------- | -------------------------------------------------------------------- | +| `OwnerNominated` | `0x906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22` | +| `OwnerClaimed` | `0xfbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f87` | + +## watcherPrecompile/WatcherPrecompile.sol + +| Event | Topic | +| ------------------- | -------------------------------------------------------------------- | +| `CalledAppGateway` | `0x255bcf22d238fe60f6611670cd7919d2bc890283be2fdaf6d2ad3411e777e33c` | +| `QueryRequested` | `0x19a4898a6390d795c9d86362d12853f4f6515b41b95a57f9b28774b4b36fc916` | +| `FinalizeRequested` | `0x17e763b469fcd8535794b9c8c1452d90597be1eeb0eb0367662e18c067fe9656` | +| `Finalized` | `0x8ee21307ed5b839a691d488e11de81bee61525893865b7ae106cc712ef42376e` | +| `PromiseResolved` | `0x3f1120f34271f52a541dfc8a71efbe6123ab80730562e8948fa7275514c41bda` | +| `TimeoutRequested` | `0xdf94fed77e41734b8a17815476bbbf88e2db15d762f42a30ddb9d7870f2fb858` | +| `TimeoutResolved` | `0x221462ec065e22637f794ec3a7edb17b2f04bec88f0546dda308bc37a83801b8` | + +## watcherPrecompile/WatcherPrecompileConfig.sol + +| Event | Topic | +| ---------------- | -------------------------------------------------------------------- | +| `PlugAdded` | `0x2cb8d865028f9abf3dc064724043264907615fadc8615a3699a85edb66472273` | +| `SwitchboardSet` | `0x6273f161f4a795e66ef3585d9b4442ef3796b32337157fdfb420b5281e4cf2e3` | + +## watcherPrecompile/WatcherPrecompileLimits.sol + +| Event | Topic | +| --------------------- | -------------------------------------------------------------------- | +| `LimitParamsUpdated` | `0x76b76501b1f65d80b7de6c1a42b2245466c1c80504052e7ad48e86b6038d39a1` | +| `AppGatewayActivated` | `0x44628d7d5628b9fbc2c84ea9bf3bd3987fa9cde8d2b28e2d5ceb451f916cb8b9` | diff --git a/contracts/apps/counter-inbox/CounterInbox.sol b/contracts/apps/counter-inbox/CounterInbox.sol index 483d67ac..df86efb7 100644 --- a/contracts/apps/counter-inbox/CounterInbox.sol +++ b/contracts/apps/counter-inbox/CounterInbox.sol @@ -5,16 +5,7 @@ import "solady/auth/Ownable.sol"; import "../../base/PlugBase.sol"; contract CounterInbox is Ownable, PlugBase { - constructor() PlugBase(msg.sender) { - _initializeOwner(msg.sender); - } - function increaseOnGateway(uint256 value_) external returns (bytes32) { return _callAppGateway(abi.encode(value_), bytes32(0)); } - - function connectSocket(address appGateway_, address socket_, address switchboard_) external { - _initializeOwner(socket_); - _connectSocket(appGateway_, socket_, switchboard_); - } } diff --git a/contracts/apps/counter/Counter.sol b/contracts/apps/counter/Counter.sol index db0e6446..44054091 100644 --- a/contracts/apps/counter/Counter.sol +++ b/contracts/apps/counter/Counter.sol @@ -7,23 +7,10 @@ import "../../base/PlugBase.sol"; contract Counter is Ownable, PlugBase { uint256 public counter; - constructor() PlugBase(msg.sender) { - _initializeOwner(msg.sender); - } - function increase() external onlySocket { counter++; } - function connectSocket( - address appGateway_, - address socket_, - address switchboard_ - ) external onlyOwner { - _initializeOwner(socket_); - _connectSocket(appGateway_, socket_, switchboard_); - } - function getCounter() external view returns (uint256) { return counter; } diff --git a/contracts/apps/counter/CounterAppGateway.sol b/contracts/apps/counter/CounterAppGateway.sol index f0998e58..dc6943cb 100644 --- a/contracts/apps/counter/CounterAppGateway.sol +++ b/contracts/apps/counter/CounterAppGateway.sol @@ -31,13 +31,13 @@ contract CounterAppGateway is AppGatewayBase { function readCounters(address[] memory instances_) public async { // the increase function is called on given list of instances - // this + _setOverrides(Read.ON, Parallel.ON); for (uint256 i = 0; i < instances_.length; i++) { uint32 chainSlug = IForwarder(instances_[i]).getChainSlug(); ICounter(instances_[i]).getCounter(); IPromise(instances_[i]).then(this.setCounterValues.selector, abi.encode(chainSlug)); } - _setOverrides(Parallel.ON); + _setOverrides(Read.OFF, Parallel.OFF); ICounter(instances_[0]).increase(); } diff --git a/contracts/apps/counter/CounterDeployer.sol b/contracts/apps/counter/CounterDeployer.sol index 93d7f115..8ae8c2f3 100644 --- a/contracts/apps/counter/CounterDeployer.sol +++ b/contracts/apps/counter/CounterDeployer.sol @@ -20,7 +20,7 @@ contract CounterDeployer is AppDeployerBase, Ownable { } function deployContracts(uint32 chainSlug_) external async { - _deploy(counter, chainSlug_); + _deploy(counter, chainSlug_, IsPlug.YES); } function initialize(uint32) public pure override { diff --git a/contracts/apps/parallel-counter/ParallelCounterDeployer.sol b/contracts/apps/parallel-counter/ParallelCounterDeployer.sol index c9c2f15c..7c4e27c5 100644 --- a/contracts/apps/parallel-counter/ParallelCounterDeployer.sol +++ b/contracts/apps/parallel-counter/ParallelCounterDeployer.sol @@ -22,14 +22,14 @@ contract ParallelCounterDeployer is AppDeployerBase, Ownable { } function deployContracts(uint32 chainSlug_) external async { - _deploy(counter1, chainSlug_); - _deploy(counter2, chainSlug_); + _deploy(counter1, chainSlug_, IsPlug.YES); + _deploy(counter2, chainSlug_, IsPlug.YES); } function deployMultiChainContracts(uint32[] memory chainSlugs_) external async { for (uint32 i = 0; i < chainSlugs_.length; i++) { - _deploy(counter1, chainSlugs_[i]); - _deploy(counter2, chainSlugs_[i]); + _deploy(counter1, chainSlugs_[i], IsPlug.YES); + _deploy(counter2, chainSlugs_[i], IsPlug.YES); } } diff --git a/contracts/apps/super-token-lockable/LimitHook.sol b/contracts/apps/super-token-lockable/LimitHook.sol index 375d9608..d273587c 100644 --- a/contracts/apps/super-token-lockable/LimitHook.sol +++ b/contracts/apps/super-token-lockable/LimitHook.sol @@ -12,10 +12,9 @@ contract LimitHook is Ownable, PlugBase { error BurnLimitExceeded(); error MintLimitExceeded(); - constructor(uint256 _burnLimit_, uint256 _mintLimit_) PlugBase(msg.sender) { + constructor(uint256 _burnLimit_, uint256 _mintLimit_) { burnLimit = _burnLimit_; mintLimit = _mintLimit_; - _initializeOwner(msg.sender); } function setLimits(uint256 _burnLimit_, uint256 _mintLimit_) external onlyOwner { diff --git a/contracts/apps/super-token-lockable/SuperTokenLockable.sol b/contracts/apps/super-token-lockable/SuperTokenLockable.sol index fcc7ffdd..abd8fa1c 100644 --- a/contracts/apps/super-token-lockable/SuperTokenLockable.sol +++ b/contracts/apps/super-token-lockable/SuperTokenLockable.sol @@ -23,9 +23,8 @@ contract SuperTokenLockable is ERC20, Ownable, PlugBase { uint8 decimals_, address initialSupplyHolder_, uint256 initialSupply_ - ) ERC20(name_, symbol_, decimals_) PlugBase(msg.sender) { + ) ERC20(name_, symbol_, decimals_) { _mint(initialSupplyHolder_, initialSupply_); - _initializeOwner(msg.sender); } function lockTokens(address user_, uint256 amount_) external onlySocket { @@ -51,20 +50,11 @@ contract SuperTokenLockable is ERC20, Ownable, PlugBase { _mint(user_, amount_); } - function setSocket(address newSocket_) external onlyOwner { + function setSocket(address newSocket_) external onlySocket { _setSocket(newSocket_); } - function setLimitHook(address limitHook_) external onlyOwner { + function setLimitHook(address limitHook_) external onlySocket { limitHook__ = LimitHook(limitHook_); } - - function connectSocket( - address appGateway_, - address socket_, - address switchboard_ - ) external onlyOwner { - _initializeOwner(socket_); - _connectSocket(appGateway_, socket_, switchboard_); - } } diff --git a/contracts/apps/super-token-lockable/SuperTokenLockableDeployer.sol b/contracts/apps/super-token-lockable/SuperTokenLockableDeployer.sol index 04dc2202..2e2d1ee7 100644 --- a/contracts/apps/super-token-lockable/SuperTokenLockableDeployer.sol +++ b/contracts/apps/super-token-lockable/SuperTokenLockableDeployer.sol @@ -49,8 +49,8 @@ contract SuperTokenLockableDeployer is AppDeployerBase, Ownable { } function deployContracts(uint32 chainSlug_) external async { - _deploy(superTokenLockable, chainSlug_); - _deploy(limitHook, chainSlug_); + _deploy(superTokenLockable, chainSlug_, IsPlug.YES); + _deploy(limitHook, chainSlug_, IsPlug.YES); } // don't need to call this directly, will be called automatically after all contracts are deployed. diff --git a/contracts/apps/super-token/SuperToken.sol b/contracts/apps/super-token/SuperToken.sol index 5ec90b0c..5bacd128 100644 --- a/contracts/apps/super-token/SuperToken.sol +++ b/contracts/apps/super-token/SuperToken.sol @@ -18,9 +18,8 @@ contract SuperToken is ERC20, Ownable, PlugBase { uint8 decimals_, address initialSupplyHolder_, uint256 initialSupply_ - ) ERC20(name_, symbol_, decimals_) PlugBase(msg.sender) { + ) ERC20(name_, symbol_, decimals_) { _mint(initialSupplyHolder_, initialSupply_); - _initializeOwner(msg.sender); } function mint(address receiver_, uint256 amount_) external onlySocket { @@ -34,13 +33,4 @@ contract SuperToken is ERC20, Ownable, PlugBase { function setSocket(address newSocket_) external onlyOwner { _setSocket(newSocket_); } - - function connectSocket( - address appGateway_, - address socket_, - address switchboard_ - ) external onlyOwner { - _initializeOwner(socket_); - _connectSocket(appGateway_, socket_, switchboard_); - } } diff --git a/contracts/apps/super-token/SuperTokenDeployer.sol b/contracts/apps/super-token/SuperTokenDeployer.sol index d1e9d51e..a8c4993c 100644 --- a/contracts/apps/super-token/SuperTokenDeployer.sol +++ b/contracts/apps/super-token/SuperTokenDeployer.sol @@ -38,7 +38,7 @@ contract SuperTokenDeployer is AppDeployerBase, Ownable { } function deployContracts(uint32 chainSlug_) external async { - _deploy(superToken, chainSlug_); + _deploy(superToken, chainSlug_, IsPlug.YES); } // no need to call this directly, will be called automatically after all contracts are deployed. diff --git a/contracts/base/AppDeployerBase.sol b/contracts/base/AppDeployerBase.sol index acde6807..b6eff74f 100644 --- a/contracts/base/AppDeployerBase.sol +++ b/contracts/base/AppDeployerBase.sol @@ -25,19 +25,34 @@ abstract contract AppDeployerBase is AppGatewayBase, IAppDeployer { /// @notice Deploys a contract /// @param contractId_ The contract ID /// @param chainSlug_ The chain slug - function _deploy(bytes32 contractId_, uint32 chainSlug_) internal { + function _deploy(bytes32 contractId_, uint32 chainSlug_, IsPlug isPlug_) internal { + _deploy(contractId_, chainSlug_, isPlug_, new bytes(0)); + } + + /// @notice Deploys a contract + /// @param contractId_ The contract ID + /// @param chainSlug_ The chain slug + function _deploy( + bytes32 contractId_, + uint32 chainSlug_, + IsPlug isPlug_, + bytes memory initCallData_ + ) internal { address asyncPromise = addressResolver__.deployAsyncPromiseContract(address(this)); isValidPromise[asyncPromise] = true; IPromise(asyncPromise).then(this.setAddress.selector, abi.encode(chainSlug_, contractId_)); onCompleteData = abi.encode(chainSlug_); IDeliveryHelper(deliveryHelper()).queue( + isPlug_, isParallelCall, chainSlug_, address(0), asyncPromise, + 0, CallType.DEPLOY, - creationCodeWithArgs[contractId_] + creationCodeWithArgs[contractId_], + initCallData_ ); } @@ -88,8 +103,7 @@ abstract contract AppDeployerBase is AppGatewayBase, IAppDeployer { /// @param chainSlug_ The chain slug /// @return socketAddress_ The socket address function getSocketAddress(uint32 chainSlug_) public view returns (address) { - return - watcherPrecompile__().appGatewayPlugs(addressResolver__.deliveryHelper(), chainSlug_); + return watcherPrecompile__().sockets(chainSlug_); } /// @notice Initializes the contract diff --git a/contracts/base/PlugBase.sol b/contracts/base/PlugBase.sol index 2c155ba8..66a1e943 100644 --- a/contracts/base/PlugBase.sol +++ b/contracts/base/PlugBase.sol @@ -5,13 +5,14 @@ import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {ISocket} from "../interfaces/ISocket.sol"; import {IPlug} from "../interfaces/IPlug.sol"; import {NotSocket} from "../protocol/utils/common/Errors.sol"; - /// @title PlugBase /// @notice Abstract contract for plugs abstract contract PlugBase is IPlug { ISocket public socket__; address public appGateway; + uint256 public isSocketInitialized; + error SocketAlreadyInitialized(); event ConnectorPlugDisconnected(); /// @notice Modifier to ensure only the socket can call the function @@ -21,8 +22,11 @@ abstract contract PlugBase is IPlug { _; } - constructor(address socket_) { - socket__ = ISocket(socket_); + /// @notice Modifier to ensure the socket is initialized + modifier socketInitializer() { + if (isSocketInitialized == 1) revert SocketAlreadyInitialized(); + isSocketInitialized = 1; + _; } /// @notice Connects the plug to the app gateway and switchboard @@ -51,4 +55,12 @@ abstract contract PlugBase is IPlug { function _callAppGateway(bytes memory payload_, bytes32 params_) internal returns (bytes32) { return socket__.callAppGateway(payload_, params_); } + + function initSocket( + address appGateway_, + address socket_, + address switchboard_ + ) external virtual socketInitializer { + _connectSocket(appGateway_, socket_, switchboard_); + } } diff --git a/contracts/interfaces/IContractFactoryPlug.sol b/contracts/interfaces/IContractFactoryPlug.sol index a7fe29ad..a3b5bac0 100644 --- a/contracts/interfaces/IContractFactoryPlug.sol +++ b/contracts/interfaces/IContractFactoryPlug.sol @@ -1,18 +1,23 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.21; +import {IsPlug} from "../protocol/utils/common/Structs.sol"; + /// @title IContractFactory /// @notice Interface for contract factory functionality interface IContractFactoryPlug { /// @notice Deploys a contract using CREATE2 + /// @param isPlug_ Whether the contract is a plug /// @param creationCode_ The contract creation code /// @param salt_ The salt value for CREATE2 /// @return address The deployed contract address function deployContract( - bytes memory creationCode_, + IsPlug isPlug_, bytes32 salt_, address appGateway_, - address switchboard_ + address switchboard_, + bytes memory creationCode_, + bytes memory initCallData_ ) external returns (address); /// @notice Gets the deterministic address for a contract deployment diff --git a/contracts/interfaces/IDeliveryHelper.sol b/contracts/interfaces/IDeliveryHelper.sol index 88873f5b..d6cd4fd3 100644 --- a/contracts/interfaces/IDeliveryHelper.sol +++ b/contracts/interfaces/IDeliveryHelper.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.3; -import {PayloadDetails, Bid, Fees, DeployParams, CallType, PayloadBatch, Parallel} from "../protocol/utils/common/Structs.sol"; +import {PayloadDetails, Bid, Fees, DeployParams, CallType, PayloadBatch, Parallel, IsPlug} from "../protocol/utils/common/Structs.sol"; interface IDeliveryHelper { event BidPlaced( @@ -20,12 +20,15 @@ interface IDeliveryHelper { function clearQueue() external; function queue( + IsPlug isPlug_, Parallel isParallel_, uint32 chainSlug_, address target_, address asyncPromise_, + uint256 value_, CallType callType_, - bytes memory payload_ + bytes memory payload_, + bytes memory initCallData_ ) external; function batch( diff --git a/contracts/interfaces/IFeesManager.sol b/contracts/interfaces/IFeesManager.sol index 641b038a..906e9ea5 100644 --- a/contracts/interfaces/IFeesManager.sol +++ b/contracts/interfaces/IFeesManager.sol @@ -4,19 +4,17 @@ pragma solidity ^0.8.3; import {Fees, Bid, PayloadDetails} from "../protocol/utils/common/Structs.sol"; interface IFeesManager { - function blockFees(address appGateway_, Fees memory fees_, Bid memory winningBid_, bytes32 asyncId_) external; - - function updateTransmitterFees( + function blockFees( + address appGateway_, + Fees memory fees_, Bid memory winningBid_, - bytes32 asyncId_, - address appGateway_ + bytes32 asyncId_ ) external; - function updateBlockedFees(bytes32 asyncId_, uint256 feesUsed_) external; - function unblockFees(bytes32 asyncId_, address appGateway_) external; function isFeesEnough(address appGateway_, Fees memory fees_) external view returns (bool); + function unblockAndAssignFees( bytes32 asyncId_, address transmitter_, diff --git a/contracts/interfaces/IPlug.sol b/contracts/interfaces/IPlug.sol index 204cadb6..1e738f09 100644 --- a/contracts/interfaces/IPlug.sol +++ b/contracts/interfaces/IPlug.sol @@ -6,5 +6,5 @@ pragma solidity ^0.8.21; * @notice Interface for a plug contract that executes the payload received from a source chain. */ interface IPlug { - function connectSocket(address appGateway_, address socket_, address switchboard_) external; + function initSocket(address appGateway_, address socket_, address switchboard_) external; } diff --git a/contracts/interfaces/ISocket.sol b/contracts/interfaces/ISocket.sol index 0e0b371b..5727abee 100644 --- a/contracts/interfaces/ISocket.sol +++ b/contracts/interfaces/ISocket.sol @@ -42,6 +42,22 @@ interface ISocket { bytes payload ); + /** + * @notice params for executing a payload + * @param payloadId the id of the payload + * @param target the address of the contract to call + * @param executionGasLimit the gas limit for the execution + * @param deadline the deadline for the execution + * @param payload the data to be executed + */ + struct ExecuteParams { + bytes32 payloadId; + address target; + uint256 executionGasLimit; + uint256 deadline; + bytes payload; + } + /** * @notice To call the appGateway on offChainVM. Should only be called by a plug. * @param payload_ bytes to be delivered to the Plug on offChainVM @@ -56,12 +72,9 @@ interface ISocket { * @notice executes a payload */ function execute( - bytes32 payloadId_, address appGateway_, - address target_, - uint256 executionGasLimit_, - bytes memory transmitterSignature_, - bytes memory payload_ + ExecuteParams memory params_, + bytes memory transmitterSignature_ ) external payable returns (bytes memory); /** diff --git a/contracts/interfaces/ISocketBatcher.sol b/contracts/interfaces/ISocketBatcher.sol index 513687b5..1626e0ae 100644 --- a/contracts/interfaces/ISocketBatcher.sol +++ b/contracts/interfaces/ISocketBatcher.sol @@ -5,6 +5,6 @@ import "../protocol/utils/common/Structs.sol"; interface ISocketBatcher { function attestAndExecute( - ExecutePayloadParams calldata params_ + AttestAndExecutePayloadParams calldata params_ ) external returns (bytes memory); } diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index a5d374b2..926c6507 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -12,6 +12,17 @@ interface IWatcherPrecompile { /// @dev Only callable by authorized addresses function setAppGateways(AppGatewayConfig[] calldata configs_) external; + /// @notice Sets up on-chain contract configurations + /// @dev Only callable by authorized addresses + function setOnChainContracts( + uint32 chainSlug_, + bytes32 sbType_, + address switchboard_, + address socket_, + address contractFactoryPlug_, + address feesPlug_ + ) 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 @@ -27,8 +38,8 @@ interface IWatcherPrecompile { /// @return payloadId The unique identifier for the request /// @return root The merkle root of the payload parameters function finalize( - FinalizeParams memory params_, - address originAppGateway_ + address originAppGateway_, + FinalizeParams memory params_ ) external returns (bytes32 payloadId, bytes32 root); /// @notice Creates a new query request @@ -50,6 +61,11 @@ interface IWatcherPrecompile { /// @param signature_ The watcher's signature function finalized(bytes32 payloadId_, bytes calldata signature_) external; + /// @notice Finalizes multiple payload execution requests with a new transmitter + /// @param payloadId_ The unique identifier of the request + /// @param params_ The parameters for finalization + function refinalize(bytes32 payloadId_, FinalizeParams memory params_) external; + /// @notice Resolves multiple promises with their return data /// @param resolvedPromises_ Array of resolved promises and their return data function resolvePromises(ResolvedPromises[] calldata resolvedPromises_) external; @@ -72,19 +88,16 @@ interface IWatcherPrecompile { /// @return root The calculated merkle root hash function getRoot(PayloadRootParams memory params_) external pure returns (bytes32 root); - /// @notice Gets the plug address for a given app gateway and chain - /// @param appGateway_ The address of the app gateway contract - /// @param chainSlug_ The identifier of the destination chain - /// @return The plug address for the given app gateway and chain - function appGatewayPlugs( - address appGateway_, - uint32 chainSlug_ - ) external view returns (address); - function setMaxTimeoutDelayInSeconds(uint256 maxTimeoutDelayInSeconds_) external; function switchboards(uint32 chainSlug_, bytes32 sbType_) external view returns (address); + function sockets(uint32 chainSlug_) external view returns (address); + + function contractFactoryPlug(uint32 chainSlug_) external view returns (address); + + function feesPlug(uint32 chainSlug_) external view returns (address); + function setIsValidInboxCaller(uint32 chainSlug_, address plug_, bool isValid_) external; function checkAndUpdateLimit( diff --git a/contracts/mock/MockSocket.sol b/contracts/mock/MockSocket.sol index bff6dc6d..11ff3930 100644 --- a/contracts/mock/MockSocket.sol +++ b/contracts/mock/MockSocket.sol @@ -1,14 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -import "../interfaces/IPlug.sol"; import {PlugDisconnected, InvalidAppGateway} from "../protocol/utils/common/Errors.sol"; import "../interfaces/ISwitchboard.sol"; import "../interfaces/ISocket.sol"; /** * @title SocketDst - * @dev SocketDst is an abstract contract that inherits from SocketBase and + * @dev SocketDst is an abstract contract that inherits from SocketUtils and * provides functionality for payload execution, verification. * It manages the mapping of payload execution status * timestamps @@ -108,15 +107,13 @@ contract MockSocket is ISocket { * @notice Executes a payload that has been delivered by transmitters and authenticated by switchboards */ function execute( - bytes32 payloadId_, address, - address target_, - uint256 executionGasLimit_, - bytes memory, - bytes memory payload_ + ExecuteParams memory params_, + bytes memory ) external payable returns (bytes memory) { // execute payload - return _execute(target_, payloadId_, executionGasLimit_, payload_); + return + _execute(params_.target, params_.payloadId, params_.executionGasLimit, params_.payload); } //////////////////////////////////////////////////////// diff --git a/contracts/mock/MockWatcherPrecompile.sol b/contracts/mock/MockWatcherPrecompile.sol index 3fe4c2ef..aa1ad265 100644 --- a/contracts/mock/MockWatcherPrecompile.sol +++ b/contracts/mock/MockWatcherPrecompile.sol @@ -29,9 +29,6 @@ contract MockWatcherPrecompile { mapping(uint32 => mapping(address => PlugConfig)) internal _plugConfigs; - /// @notice Maps app gateway to their associated plugs per network - /// @dev appGateway => chainSlug => plug - mapping(address => mapping(uint32 => address)) public appGatewayPlugs; /// @notice Error thrown when an invalid chain slug is provided error InvalidChainSlug(); error InvalidTransmitter(); @@ -141,11 +138,13 @@ contract MockWatcherPrecompile { emit FinalizeRequested( payloadId, AsyncRequest( + msg.sender, address(0), address(0), params_.payloadDetails.target, address(0), 0, + block.timestamp + 1000, params_.asyncId, bytes32(0), bytes(""), @@ -229,19 +228,6 @@ contract MockWatcherPrecompile { return bytes32((uint256(uint160(address(this))) << 64) | timeoutCounter_); } - function setAppGateways(AppGatewayConfig[] calldata configs) external { - for (uint256 i = 0; i < configs.length; i++) { - // Store the plug configuration for this network and plug - _plugConfigs[configs[i].chainSlug][configs[i].plug] = PlugConfig({ - appGateway: configs[i].appGateway, - switchboard: configs[i].switchboard - }); - - // Create reverse mapping from app gateway to plug for easy lookup - appGatewayPlugs[configs[i].appGateway][configs[i].chainSlug] = configs[i].plug; - } - } - /// @notice Retrieves the configuration for a specific plug on a network /// @param chainSlug_ The identifier of the network /// @param plug_ The address of the plug diff --git a/contracts/protocol/Forwarder.sol b/contracts/protocol/Forwarder.sol index 54c7d64e..7e01c67c 100644 --- a/contracts/protocol/Forwarder.sol +++ b/contracts/protocol/Forwarder.sol @@ -23,7 +23,6 @@ contract Forwarder is IForwarder, Initializable { /// @notice caches the latest async promise address for the last call address public latestAsyncPromise; - uint64 public version; constructor() { _disableInitializers(); // disable for implementation @@ -86,12 +85,15 @@ contract Forwarder is IForwarder, Initializable { // Queue the call in the auction house. IDeliveryHelper(deliveryHelper).queue( + IsPlug.NO, isParallelCall, chainSlug, onChainAddress, latestAsyncPromise, + 0, isReadCall == Read.ON ? CallType.READ : CallType.WRITE, - msg.data + msg.data, + bytes("") ); } diff --git a/contracts/protocol/payload-delivery/ContractFactoryPlug.sol b/contracts/protocol/payload-delivery/ContractFactoryPlug.sol index c1cb1c98..6e137162 100644 --- a/contracts/protocol/payload-delivery/ContractFactoryPlug.sol +++ b/contracts/protocol/payload-delivery/ContractFactoryPlug.sol @@ -6,25 +6,30 @@ import {RESCUE_ROLE} from "../utils/common/AccessRoles.sol"; import "../utils/RescueFundsLib.sol"; import {NotSocket} from "../utils/common/Errors.sol"; import "../../base/PlugBase.sol"; +import "../../interfaces/IContractFactoryPlug.sol"; /// @title ContractFactory /// @notice Abstract contract for deploying contracts -contract ContractFactoryPlug is PlugBase, AccessControl { - event Deployed(address addr, bytes32 salt); +contract ContractFactoryPlug is PlugBase, AccessControl, IContractFactoryPlug { + event Deployed(address addr, bytes32 salt, bytes returnData); /// @notice Error thrown if it failed to deploy the create2 contract error DeploymentFailed(); + error ExecutionFailed(); - constructor(address socket_, address owner_) PlugBase(socket_) { + constructor(address socket_, address owner_) { _initializeOwner(owner_); + _setSocket(socket_); } function deployContract( - bytes memory creationCode_, + IsPlug isPlug_, bytes32 salt_, address appGateway_, - address switchboard_ - ) public returns (address) { + address switchboard_, + bytes memory creationCode_, + bytes memory initCallData_ + ) public override returns (address) { if (msg.sender != address(socket__)) { revert NotSocket(); } @@ -38,8 +43,27 @@ contract ContractFactoryPlug is PlugBase, AccessControl { } } - IPlug(addr).connectSocket(appGateway_, msg.sender, switchboard_); - emit Deployed(addr, salt_); + if (isPlug_ == IsPlug.YES) IPlug(addr).initSocket(appGateway_, msg.sender, switchboard_); + + bytes memory returnData; + if (initCallData_.length > 0) { + // Capture more detailed error information + (bool success, bytes memory returnData_) = addr.call(initCallData_); + + if (!success) { + // Additional error logging + assembly { + let ptr := mload(0x40) + returndatacopy(ptr, 0, returndatasize()) + log0(ptr, returndatasize()) + } + + revert ExecutionFailed(); + } + returnData = returnData_; + } + + emit Deployed(addr, salt_, returnData); return addr; } diff --git a/contracts/protocol/payload-delivery/FeesPlug.sol b/contracts/protocol/payload-delivery/FeesPlug.sol index ced1b5c2..a6a1afdf 100644 --- a/contracts/protocol/payload-delivery/FeesPlug.sol +++ b/contracts/protocol/payload-delivery/FeesPlug.sol @@ -7,12 +7,14 @@ import "../utils/AccessControl.sol"; import {RESCUE_ROLE} from "../utils/common/AccessRoles.sol"; import "../utils/RescueFundsLib.sol"; import {ETH_ADDRESS} from "../utils/common/Constants.sol"; +import {InvalidTokenAddress} from "../utils/common/Errors.sol"; /// @title FeesManager /// @notice Abstract contract for managing fees contract FeesPlug is PlugBase, AccessControl { mapping(address => uint256) public balanceOf; mapping(bytes32 => bool) public feesRedeemed; + mapping(address => bool) public whitelistedTokens; /// @notice Error thrown when attempting to pay fees again error FeesAlreadyPaid(); @@ -20,20 +22,26 @@ contract FeesPlug is PlugBase, AccessControl { error InsufficientTokenBalance(address token_); /// @notice Error thrown when deposit amount does not match msg.value error InvalidDepositAmount(); - error InvalidTokenAddress(); + error TokenNotWhitelisted(address token_); /// @notice Event emitted when fees are deposited event FeesDeposited(address appGateway, address token, uint256 amount); /// @notice Event emitted when fees are withdrawn event FeesWithdrawn(address token, uint256 amount, address receiver); + /// @notice Event emitted when a token is whitelisted + event TokenWhitelisted(address token); + /// @notice Event emitted when a token is removed from whitelist + event TokenRemovedFromWhitelist(address token); modifier isFeesEnough(uint256 fee_, address feeToken_) { if (balanceOf[feeToken_] < fee_) revert InsufficientTokenBalance(feeToken_); _; } - constructor(address socket_, address owner_) PlugBase(socket_) { + constructor(address socket_, address owner_) { + _setSocket(socket_); _initializeOwner(owner_); + whitelistedTokens[ETH_ADDRESS] = true; // ETH is whitelisted by default } function distributeFee( @@ -65,6 +73,8 @@ contract FeesPlug is PlugBase, AccessControl { /// @param amount_ The amount /// @param appGateway_ The app gateway address function deposit(address token_, address appGateway_, uint256 amount_) external payable { + if (!whitelistedTokens[token_]) revert TokenNotWhitelisted(token_); + if (token_ == ETH_ADDRESS) { if (msg.value != amount_) revert InvalidDepositAmount(); } else { @@ -100,6 +110,21 @@ contract FeesPlug is PlugBase, AccessControl { _connectSocket(appGateway_, socket_, switchboard_); } + /// @notice Adds a token to the whitelist + /// @param token_ The token address to whitelist + function whitelistToken(address token_) external onlyOwner { + whitelistedTokens[token_] = true; + emit TokenWhitelisted(token_); + } + + /// @notice Removes a token from the whitelist + /// @param token_ The token address to remove + function removeTokenFromWhitelist(address token_) external onlyOwner { + if (token_ == ETH_ADDRESS) revert(); // Cannot remove ETH from whitelist + whitelistedTokens[token_] = false; + emit TokenRemovedFromWhitelist(token_); + } + /** * @notice Rescues funds from the contract if they are locked by mistake. This contract does not * theoretically need this function but it is added for safety. diff --git a/contracts/protocol/payload-delivery/app-gateway/AuctionManager.sol b/contracts/protocol/payload-delivery/app-gateway/AuctionManager.sol index 76a8c2ce..59bad963 100644 --- a/contracts/protocol/payload-delivery/app-gateway/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/app-gateway/AuctionManager.sol @@ -20,7 +20,6 @@ contract AuctionManager is AddressResolverUtil, Ownable, IAuctionManager, Initia mapping(bytes32 => bool) public override auctionStarted; uint256 public auctionEndDelaySeconds; - uint64 public version; /// @notice Error thrown when trying to start or bid a closed auction error AuctionClosed(); @@ -33,6 +32,8 @@ contract AuctionManager is AddressResolverUtil, Ownable, IAuctionManager, Initia /// @notice Error thrown if a lower bid already exists error LowerBidAlreadyExists(); + event AuctionRestarted(bytes32 asyncId); + constructor() { _disableInitializers(); // disable for implementation } @@ -50,7 +51,6 @@ contract AuctionManager is AddressResolverUtil, Ownable, IAuctionManager, Initia ) public reinitializer(1) { _setAddressResolver(addressResolver_); _initializeOwner(owner_); - version = 1; vmChainSlug = vmChainSlug_; auctionEndDelaySeconds = auctionEndDelaySeconds_; } @@ -119,8 +119,6 @@ contract AuctionManager is AddressResolverUtil, Ownable, IAuctionManager, Initia emit BidPlaced(asyncId_, newBid); auctionClosed[asyncId_] = true; - - emit AuctionEnded(asyncId_, newBid); } /// @notice Ends an auction @@ -149,20 +147,20 @@ contract AuctionManager is AddressResolverUtil, Ownable, IAuctionManager, Initia asyncId_, winningBid ); - - // todo: add scheduler for a time to retry auction } function expireBid(bytes32 asyncId_) external onlyWatcherPrecompile { PayloadBatch memory batch = IDeliveryHelper(addressResolver__.deliveryHelper()) .payloadBatches(asyncId_); + // if executed, bid is not expired - // todo: should be less than total payloads in batch or zero? - if (batch.totalPayloadsRemaining == 0) return; + if (batch.totalPayloadsRemaining == 0 || batch.isBatchCancelled) return; IFeesManager(addressResolver__.feesManager()).unblockFees(asyncId_, batch.appGateway); winningBids[asyncId_] = Bid({fee: 0, transmitter: address(0), extraData: ""}); auctionClosed[asyncId_] = false; + + emit AuctionRestarted(asyncId_); } function _recoverSigner( diff --git a/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol b/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol index 14a636af..43a34ddd 100644 --- a/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol @@ -91,7 +91,6 @@ abstract contract BatchAsync is QueueAsync { bytes32 asyncId = getCurrentAsyncId(); asyncCounter++; - if (!IFeesManager(addressResolver__.feesManager()).isFeesEnough(msg.sender, fees_)) revert InsufficientFees(); @@ -185,7 +184,6 @@ abstract contract BatchAsync is QueueAsync { if (payloadDetails_[i].callType == CallType.DEPLOY) { // contract factory plug deploys new contracts payloadDetails_[i].target = getDeliveryHelperPlugAddress( - address(this), payloadDetails_[i].chainSlug ); writes++; @@ -227,6 +225,7 @@ abstract contract BatchAsync is QueueAsync { isBatchCancelled: false, totalPayloadsRemaining: _payloadBatches[asyncId].totalPayloadsRemaining, lastBatchPromises: _payloadBatches[asyncId].lastBatchPromises, + lastBatchOfPayloads: new bytes32[](0), onCompleteData: onCompleteData_ }); @@ -278,11 +277,8 @@ abstract contract BatchAsync is QueueAsync { /// @notice Gets the payload delivery plug address /// @param chainSlug_ The chain identifier /// @return address The address of the payload delivery plug - function getDeliveryHelperPlugAddress( - address appGateway_, - uint32 chainSlug_ - ) public view returns (address) { - return watcherPrecompile__().appGatewayPlugs(appGateway_, chainSlug_); + function getDeliveryHelperPlugAddress(uint32 chainSlug_) public view returns (address) { + return watcherPrecompile__().contractFactoryPlug(chainSlug_); } /// @notice Gets the current async ID diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol index 16e817d6..650d622f 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol @@ -1,19 +1,20 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {Ownable} from "solady/auth/Ownable.sol"; import "solady/utils/Initializable.sol"; import {IAppGateway} from "../../../interfaces/IAppGateway.sol"; import {Bid, PayloadBatch, Fees, PayloadDetails, FinalizeParams} from "../../../protocol/utils/common/Structs.sol"; import {DISTRIBUTE_FEE, DEPLOY} from "../../../protocol/utils/common/Constants.sol"; -import {PromisesNotResolved} from "../../../protocol/utils/common/Errors.sol"; +import {PromisesNotResolved, InvalidTransmitter} from "../../../protocol/utils/common/Errors.sol"; import "./BatchAsync.sol"; -contract DeliveryHelper is BatchAsync, Ownable, Initializable { +contract DeliveryHelper is BatchAsync, Initializable { event CallBackReverted(bytes32 asyncId_, bytes32 payloadId_); uint64 public version; + bytes32[] public tempPayloadIds; + constructor() { _disableInitializers(); // disable for implementation } @@ -36,32 +37,33 @@ contract DeliveryHelper is BatchAsync, Ownable, Initializable { bytes32 asyncId_, Bid memory winningBid_ ) external onlyAuctionManager(asyncId_) { - _payloadBatches[asyncId_].winningBid = winningBid_; + if (winningBid_.transmitter == address(0)) revert InvalidTransmitter(); - // update fees - IFeesManager(addressResolver__.feesManager()).updateTransmitterFees( - winningBid_, - asyncId_, - _payloadBatches[asyncId_].appGateway - ); + bool isRestarted = _payloadBatches[asyncId_].winningBid.transmitter != address(0); + _payloadBatches[asyncId_].winningBid = winningBid_; - if (winningBid_.transmitter != address(0)) { - // process batch - _process(asyncId_); - } else { - // todo: check if this is correct? - // cancel batch - _payloadBatches[asyncId_].isBatchCancelled = true; - emit BatchCancelled(asyncId_); + if (!isRestarted) return _process(asyncId_, false); + + // Refinalize all payloads in the batch if a new transmitter is assigned + bytes32[] memory payloadIds = _payloadBatches[asyncId_].lastBatchOfPayloads; + for (uint256 i = 0; i < payloadIds.length; i++) { + watcherPrecompile__().refinalize( + payloadIds[i], + FinalizeParams({ + payloadDetails: payloadIdToPayloadDetails[payloadIds[i]], + asyncId: asyncId_, + transmitter: winningBid_.transmitter + }) + ); } } function callback(bytes memory asyncId_, bytes memory) external override onlyPromises { bytes32 asyncId = abi.decode(asyncId_, (bytes32)); - _process(asyncId); + _process(asyncId, true); } - function _process(bytes32 asyncId_) internal { + function _process(bytes32 asyncId_, bool isCallback_) internal { PayloadBatch storage payloadBatch = _payloadBatches[asyncId_]; if (payloadBatch.isBatchCancelled) return; @@ -71,16 +73,21 @@ contract DeliveryHelper is BatchAsync, Ownable, Initializable { // Check if all promises are resolved for (uint256 i = 0; i < payloadBatch.lastBatchPromises.length; i++) { if (!IPromise(payloadBatch.lastBatchPromises[i]).resolved()) { - revert PromisesNotResolved(); + if (isCallback_) revert PromisesNotResolved(); } } // Clear promises array after all are resolved - delete payloadBatch.lastBatchPromises; + if (isCallback_) { + delete payloadBatch.lastBatchPromises; + } } if (payloadBatch.totalPayloadsRemaining > 0) { + delete tempPayloadIds; + // Proceed with next payload only if all promises are resolved _finalizeNextPayload(asyncId_); + payloadBatch.lastBatchOfPayloads = tempPayloadIds; } else { _finishBatch(asyncId_, payloadBatch); } @@ -103,10 +110,8 @@ contract DeliveryHelper is BatchAsync, Ownable, Initializable { PayloadDetails[] storage payloads = payloadBatchDetails[asyncId_]; // Check for empty payloads or index out of bounds - // todo: should revert if (payloads.length == 0 || currentIndex >= payloads.length) { - _finishBatch(asyncId_, payloadBatch_); - return; + revert InvalidIndex(); } // Deploy single promise for the next batch of operations @@ -156,11 +161,12 @@ contract DeliveryHelper is BatchAsync, Ownable, Initializable { transmitter: payloadBatch_.winningBid.transmitter }); (payloadId, root) = watcherPrecompile__().finalize( - finalizeParams, - payloadBatch_.appGateway + payloadBatch_.appGateway, + finalizeParams ); } + tempPayloadIds.push(payloadId); payloadIdToBatchHash[payloadId] = asyncId_; payloadIdToPayloadDetails[payloadId] = payloadDetails_; diff --git a/contracts/protocol/payload-delivery/app-gateway/FeesManager.sol b/contracts/protocol/payload-delivery/app-gateway/FeesManager.sol index 48a43f66..50fbea34 100644 --- a/contracts/protocol/payload-delivery/app-gateway/FeesManager.sol +++ b/contracts/protocol/payload-delivery/app-gateway/FeesManager.sol @@ -11,12 +11,12 @@ import {FORWARD_CALL, DISTRIBUTE_FEE, DEPLOY, WITHDRAW} from "../../../protocol/ import {IFeesPlug} from "../../../interfaces/IFeesPlug.sol"; import {IFeesManager} from "../../../interfaces/IFeesManager.sol"; +import {NotAuctionManager} from "../../../protocol/utils/common/Errors.sol"; + /// @title FeesManager /// @notice Contract for managing fees contract FeesManager is IFeesManager, AddressResolverUtil, Ownable, Initializable { uint256 public feesCounter; - mapping(uint32 => uint256) public feeCollectionGasLimit; - uint64 public version; /// @notice Struct containing fee amounts and status struct TokenBalance { @@ -101,7 +101,6 @@ contract FeesManager is IFeesManager, AddressResolverUtil, Ownable, Initializabl /// @param addressResolver_ The address of the address resolver /// @param owner_ The address of the owner function initialize(address addressResolver_, address owner_) public reinitializer(1) { - version = 1; _setAddressResolver(addressResolver_); _initializeOwner(owner_); } @@ -151,58 +150,51 @@ contract FeesManager is IFeesManager, AddressResolverUtil, Ownable, Initializabl /// @notice Blocks fees for transmitter /// @param appGateway_ The app gateway address - /// @param fees_ The fees data struct + /// @param feesGivenByApp_ The fees data struct given by the app gateway /// @param asyncId_ The batch identifier /// @dev Only callable by delivery helper function blockFees( address appGateway_, - Fees memory fees_, + Fees memory feesGivenByApp_, Bid memory winningBid_, bytes32 asyncId_ ) external { - // todo: only auction manager can call this + if (msg.sender != deliveryHelper().getAsyncBatchDetails(asyncId_).auctionManager) + revert NotAuctionManager(); + address appGateway = _getCoreAppGateway(appGateway_); // Block fees uint256 availableFees = getAvailableFees( - fees_.feePoolChain, + feesGivenByApp_.feePoolChain, appGateway, - fees_.feePoolToken + feesGivenByApp_.feePoolToken ); - if (availableFees < winningBid_.fee) revert InsufficientFeesAvailable(); - - TokenBalance storage tokenBalance = appGatewayFeeBalances[appGateway][fees_.feePoolChain][ - fees_.feePoolToken - ]; - tokenBalance.blocked += winningBid_.fee; - asyncIdBlockedFees[asyncId_] = fees_; - emit FeesBlocked(asyncId_, fees_.feePoolChain, fees_.feePoolToken, winningBid_.fee); - } - - function updateTransmitterFees( - Bid memory winningBid_, - bytes32 asyncId_, - address appGateway_ - ) external onlyDeliveryHelper { - address appGateway = _getCoreAppGateway(appGateway_); + if (asyncIdBlockedFees[asyncId_].amount > 0) + availableFees += asyncIdBlockedFees[asyncId_].amount; - Fees storage fees = asyncIdBlockedFees[asyncId_]; - TokenBalance storage tokenBalance = appGatewayFeeBalances[appGateway][fees.feePoolChain][ - fees.feePoolToken - ]; - - // if no transmitter assigned after auction, unblock fees - if (winningBid_.transmitter == address(0)) { - tokenBalance.blocked -= fees.amount; - delete asyncIdBlockedFees[asyncId_]; - return; - } - - // update new amount - fees.amount = winningBid_.fee; - tokenBalance.blocked = tokenBalance.blocked - fees.amount + winningBid_.fee; + if (availableFees < winningBid_.fee) revert InsufficientFeesAvailable(); + TokenBalance storage tokenBalance = appGatewayFeeBalances[appGateway][ + feesGivenByApp_.feePoolChain + ][feesGivenByApp_.feePoolToken]; + + tokenBalance.blocked = + tokenBalance.blocked + + winningBid_.fee - + asyncIdBlockedFees[asyncId_].amount; + + asyncIdBlockedFees[asyncId_] = Fees({ + feePoolChain: feesGivenByApp_.feePoolChain, + feePoolToken: feesGivenByApp_.feePoolToken, + amount: winningBid_.fee + }); - emit TransmitterFeesUpdated(asyncId_, winningBid_.transmitter, winningBid_.fee); + emit FeesBlocked( + asyncId_, + feesGivenByApp_.feePoolChain, + feesGivenByApp_.feePoolToken, + winningBid_.fee + ); } /// @notice Unblocks fees after successful execution and assigns them to the transmitter @@ -275,15 +267,13 @@ contract FeesManager is IFeesManager, AddressResolverUtil, Ownable, Initializabl // Create payload for plug contract payloadDetails = _createPayloadDetails(CallType.WRITE, chainSlug_, payload); - - // todo: revisit FinalizeParams memory finalizeParams = FinalizeParams({ payloadDetails: payloadDetails, asyncId: bytes32(0), transmitter: transmitter }); - (payloadId, root) = watcherPrecompile__().finalize(finalizeParams, address(this)); + (payloadId, root) = watcherPrecompile__().finalize(address(this), finalizeParams); } function _createPayloadDetails( @@ -298,35 +288,13 @@ contract FeesManager is IFeesManager, AddressResolverUtil, Ownable, Initializabl target: _getFeesPlugAddress(chainSlug_), payload: payload_, callType: callType_, + value: 0, executionGasLimit: 1000000, next: new address[](2), isParallel: Parallel.OFF }); } - /// @notice Updates blocked fees in case of failed execution - /// @param asyncId_ The batch identifier - /// @dev Only callable by delivery helper - function updateBlockedFees(bytes32 asyncId_, uint256 feesUsed_) external onlyOwner { - PayloadBatch memory batch = IDeliveryHelper(deliveryHelper()).getAsyncBatchDetails( - asyncId_ - ); - - Fees storage fees = asyncIdBlockedFees[asyncId_]; - TokenBalance storage tokenBalance = appGatewayFeeBalances[batch.appGateway][ - batch.fees.feePoolChain - ][batch.fees.feePoolToken]; - - // todo how to settle fees here? - // Unblock unused fees - uint256 unusedFees = fees.amount - feesUsed_; - tokenBalance.blocked -= unusedFees; - - // Update fees with actual fees used - fees.amount = feesUsed_; - asyncIdBlockedFees[asyncId_] = fees; - } - /// @notice Withdraws funds to a specified receiver /// @dev This function is used to withdraw fees from the fees plug /// @param appGateway_ The address of the app gateway @@ -365,6 +333,6 @@ contract FeesManager is IFeesManager, AddressResolverUtil, Ownable, Initializabl } function _getFeesPlugAddress(uint32 chainSlug_) internal view returns (address) { - return watcherPrecompile__().appGatewayPlugs(address(this), chainSlug_); + return watcherPrecompile__().feesPlug(chainSlug_); } } diff --git a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol index 7fea4890..85f13c65 100644 --- a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.21; import {AddressResolverUtil} from "../../../protocol/utils/AddressResolverUtil.sol"; -import {CallParams, Fees, PayloadDetails, CallType, Bid, PayloadBatch, Parallel} from "../../../protocol/utils/common/Structs.sol"; +import {CallParams, Fees, PayloadDetails, CallType, Bid, PayloadBatch, Parallel, IsPlug} from "../../../protocol/utils/common/Structs.sol"; import {NotAuctionManager, InvalidPromise, InvalidIndex} from "../../../protocol/utils/common/Errors.sol"; import {AsyncPromise} from "../../AsyncPromise.sol"; import {IPromise} from "../../../interfaces/IPromise.sol"; @@ -10,9 +10,10 @@ import {IAppDeployer} from "../../../interfaces/IAppDeployer.sol"; import {IAddressResolver} from "../../../interfaces/IAddressResolver.sol"; import {IContractFactoryPlug} from "../../../interfaces/IContractFactoryPlug.sol"; import {IDeliveryHelper} from "../../../interfaces/IDeliveryHelper.sol"; +import {Ownable} from "solady/auth/Ownable.sol"; /// @notice Abstract contract for managing asynchronous payloads -abstract contract QueueAsync is AddressResolverUtil, IDeliveryHelper { +abstract contract QueueAsync is AddressResolverUtil, IDeliveryHelper, Ownable { uint256 public saltCounter; uint256 public asyncCounter; @@ -30,7 +31,8 @@ abstract contract QueueAsync is AddressResolverUtil, IDeliveryHelper { // asyncId => PayloadBatch mapping(bytes32 => PayloadBatch) internal _payloadBatches; - event PayloadBatchCancelled(bytes32 asyncId_); + event PayloadBatchCancelled(bytes32 asyncId); + event BidTimeoutUpdated(uint256 newBidTimeout); function payloadBatches(bytes32 asyncId_) external view override returns (PayloadBatch memory) { return _payloadBatches[asyncId_]; @@ -65,23 +67,29 @@ abstract contract QueueAsync is AddressResolverUtil, IDeliveryHelper { /// @param callType_ The call type /// @param payload_ The payload function queue( + IsPlug isPlug_, Parallel isParallel_, uint32 chainSlug_, address target_, address asyncPromise_, + uint256 value_, CallType callType_, - bytes memory payload_ + bytes memory payload_, + bytes memory initCallData_ ) external { // todo: sb related details callParamsArray.push( CallParams({ + isPlug: isPlug_, callType: callType_, asyncPromise: asyncPromise_, chainSlug: chainSlug_, target: target_, payload: payload_, + value: value_, gasLimit: 10000000, - isParallel: isParallel_ + isParallel: isParallel_, + initCallData: initCallData_ }) ); } @@ -128,10 +136,12 @@ abstract contract QueueAsync is AddressResolverUtil, IDeliveryHelper { // app gateway is set in the plug deployed on chain payload_ = abi.encodeWithSelector( IContractFactoryPlug.deployContract.selector, - payload_, + params_.isPlug, salt_, appGatewayForPlug_, - switchboard_ + switchboard_, + payload_, + params_.initCallData ); // for deploy, we set delivery helper as app gateway of contract factory plug @@ -143,6 +153,7 @@ abstract contract QueueAsync is AddressResolverUtil, IDeliveryHelper { appGateway: appGateway_, chainSlug: params_.chainSlug, target: params_.target, + value: params_.value, payload: payload_, callType: params_.callType, executionGasLimit: params_.gasLimit == 0 ? 1_000_000 : params_.gasLimit, @@ -151,6 +162,13 @@ abstract contract QueueAsync is AddressResolverUtil, IDeliveryHelper { }); } + /// @notice Updates the bid timeout + /// @param newBidTimeout_ The new bid timeout value + function updateBidTimeout(uint256 newBidTimeout_) external onlyOwner { + bidTimeout = newBidTimeout_; + emit BidTimeoutUpdated(newBidTimeout_); + } + function getPayloadIndexDetails( bytes32 asyncId_, uint256 index_ diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 7102c533..7de9a6ff 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -1,19 +1,18 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -import "../../interfaces/IPlug.sol"; -import "./SocketBase.sol"; +import "./SocketUtils.sol"; import {PlugDisconnected, InvalidAppGateway} from "../utils/common/Errors.sol"; /** * @title SocketDst - * @dev SocketDst is an abstract contract that inherits from SocketBase and + * @dev SocketDst is an abstract contract that inherits from SocketUtils and * provides functionality for payload execution, verification. * It manages the mapping of payload execution status * timestamps * It also includes functions for payload execution and verification */ -contract Socket is SocketBase { +contract Socket is SocketUtils { //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// //////////////////////////////////////////////////////// @@ -41,6 +40,7 @@ contract Socket is SocketBase { error LowGasLimit(); error InvalidSlug(); error ExecutionFailed(); + error DeadlinePassed(); //////////////////////////////////////////////////////////// ////////////////////// State Vars ////////////////////////// @@ -56,12 +56,11 @@ contract Socket is SocketBase { uint32 chainSlug_, address owner_, string memory version_ - ) SocketBase(chainSlug_, owner_, version_) {} + ) SocketUtils(chainSlug_, owner_, version_) {} //////////////////////////////////////////////////////// ////////////////////// OPERATIONS ////////////////////////// //////////////////////////////////////////////////////// - /** * @notice To send message to a connected remote chain. Should only be called by a plug. * @param payload bytes to be delivered to the Plug on the siblingChainSlug_ @@ -92,49 +91,50 @@ contract Socket is SocketBase { * @notice Executes a payload that has been delivered by transmitters and authenticated by switchboards */ function execute( - bytes32 payloadId_, address appGateway_, - address target_, - uint256 executionGasLimit_, - bytes memory transmitterSignature_, - bytes memory payload_ + ExecuteParams memory params_, + bytes memory transmitterSignature_ ) external payable returns (bytes memory) { // make sure payload is not executed already - if (payloadExecuted[payloadId_]) revert PayloadAlreadyExecuted(); + if (payloadExecuted[params_.payloadId]) revert PayloadAlreadyExecuted(); // update state to make sure no reentrancy - payloadExecuted[payloadId_] = true; + payloadExecuted[params_.payloadId] = true; + + if (params_.deadline < block.timestamp) revert DeadlinePassed(); // extract plug address from msgID - address switchboard = _decodeSwitchboard(payloadId_); - uint32 localSlug = _decodeChainSlug(payloadId_); + address switchboard = _decodeSwitchboard(params_.payloadId); + uint32 localSlug = _decodeChainSlug(params_.payloadId); - PlugConfig memory plugConfig = _plugConfigs[target_]; + PlugConfig memory plugConfig = _plugConfigs[params_.target]; if (switchboard != address(plugConfig.switchboard__)) revert InvalidSwitchboard(); - if (localSlug != chainSlug) revert InvalidSlug(); address transmitter = _recoverSigner( - keccak256(abi.encode(address(this), payloadId_)), + keccak256(abi.encode(address(this), params_.payloadId)), transmitterSignature_ ); // create packed payload bytes32 root = _packPayload( - payloadId_, + params_.payloadId, appGateway_, transmitter, - target_, - executionGasLimit_, - payload_ + params_.target, + msg.value, + params_.deadline, + params_.executionGasLimit, + params_.payload ); // verify payload was part of the packet and // authenticated by respective switchboard - _verify(root, payloadId_, ISwitchboard(switchboard)); + _verify(root, params_.payloadId, ISwitchboard(switchboard)); // execute payload - return _execute(target_, payloadId_, executionGasLimit_, payload_); + return + _execute(params_.target, params_.payloadId, params_.executionGasLimit, params_.payload); } //////////////////////////////////////////////////////// @@ -158,6 +158,7 @@ contract Socket is SocketBase { bytes memory payload_ ) internal returns (bytes memory) { if (gasleft() < executionGasLimit_) revert LowGasLimit(); + // NOTE: external un-trusted call (bool success, bytes memory returnData) = localPlug_.call{ gas: executionGasLimit_, diff --git a/contracts/protocol/socket/SocketBatcher.sol b/contracts/protocol/socket/SocketBatcher.sol index 685e2111..96371877 100644 --- a/contracts/protocol/socket/SocketBatcher.sol +++ b/contracts/protocol/socket/SocketBatcher.sol @@ -5,7 +5,7 @@ import "solady/auth/Ownable.sol"; import "../../interfaces/ISocket.sol"; import "../../interfaces/ISwitchboard.sol"; import "../utils/RescueFundsLib.sol"; -import {ExecutePayloadParams} from "../../protocol/utils/common/Structs.sol"; +import {AttestAndExecutePayloadParams} from "../../protocol/utils/common/Structs.sol"; /** * @title SocketBatcher @@ -26,21 +26,26 @@ contract SocketBatcher is Ownable { } function attestAndExecute( - ExecutePayloadParams calldata params_ - ) external returns (bytes memory) { + AttestAndExecutePayloadParams calldata params_ + ) external payable returns (bytes memory) { ISwitchboard(params_.switchboard).attest( params_.payloadId, params_.root, params_.watcherSignature ); + + ISocket.ExecuteParams memory executeParams = ISocket.ExecuteParams({ + payloadId: params_.payloadId, + target: params_.target, + executionGasLimit: params_.executionGasLimit, + deadline: params_.deadline, + payload: params_.payload + }); return - socket__.execute( - params_.payloadId, + socket__.execute{value: msg.value}( params_.appGateway, - params_.target, - params_.executionGasLimit, - params_.transmitterSignature, - params_.payload + executeParams, + params_.transmitterSignature ); } diff --git a/contracts/protocol/socket/SocketConfig.sol b/contracts/protocol/socket/SocketConfig.sol index 50a56f35..4ff8a904 100644 --- a/contracts/protocol/socket/SocketConfig.sol +++ b/contracts/protocol/socket/SocketConfig.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.21; import "../../interfaces/ISocket.sol"; import "../../interfaces/ISwitchboard.sol"; import "../utils/AccessControl.sol"; -import {RESCUE_ROLE} from "../utils/common/AccessRoles.sol"; +import {GOVERNANCE_ROLE, RESCUE_ROLE} from "../utils/common/AccessRoles.sol"; /** * @title SocketConfig @@ -23,8 +23,14 @@ abstract contract SocketConfig is ISocket, AccessControl { ISwitchboard switchboard__; } + enum SwitchboardStatus { + NOT_REGISTERED, + REGISTERED, + DISABLED + } + // Error triggered when a switchboard already exists - mapping(address => bool) public isValidSwitchboard; + mapping(address => SwitchboardStatus) public isValidSwitchboard; // plug => (appGateway, switchboard__) mapping(address => PlugConfig) internal _plugConfigs; @@ -33,21 +39,32 @@ abstract contract SocketConfig is ISocket, AccessControl { // Error triggered when a connection is invalid error InvalidConnection(); error InvalidSwitchboard(); + error SwitchboardExistsOrDisabled(); // Event triggered when a new switchboard is added event SwitchboardAdded(address switchboard); + event SwitchboardDisabled(address switchboard); function registerSwitchboard() external { - if (isValidSwitchboard[msg.sender]) revert SwitchboardExists(); - isValidSwitchboard[msg.sender] = true; + if (isValidSwitchboard[msg.sender] != SwitchboardStatus.NOT_REGISTERED) + revert SwitchboardExistsOrDisabled(); + + isValidSwitchboard[msg.sender] = SwitchboardStatus.REGISTERED; emit SwitchboardAdded(msg.sender); } + function disableSwitchboard() external onlyRole(GOVERNANCE_ROLE) { + isValidSwitchboard[msg.sender] = SwitchboardStatus.DISABLED; + emit SwitchboardDisabled(msg.sender); + } + /** * @notice connects Plug to Socket and sets the config for given `siblingChainSlug_` */ function connect(address appGateway_, address switchboard_) external override { - if (!isValidSwitchboard[switchboard_]) revert InvalidSwitchboard(); + if (isValidSwitchboard[switchboard_] != SwitchboardStatus.REGISTERED) + revert InvalidSwitchboard(); + PlugConfig storage _plugConfig = _plugConfigs[msg.sender]; _plugConfig.appGateway = appGateway_; diff --git a/contracts/protocol/socket/SocketBase.sol b/contracts/protocol/socket/SocketUtils.sol similarity index 95% rename from contracts/protocol/socket/SocketBase.sol rename to contracts/protocol/socket/SocketUtils.sol index 7bc97d69..e46dd0fb 100644 --- a/contracts/protocol/socket/SocketBase.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -6,11 +6,11 @@ import "./SocketConfig.sol"; import {ECDSA} from "solady/utils/ECDSA.sol"; /** - * @title SocketBase + * @title SocketUtils * @notice A contract that is responsible for common storage for src and dest contracts, governance * setters and inherits SocketConfig */ -abstract contract SocketBase is SocketConfig { +abstract contract SocketUtils is SocketConfig { // Version string for this socket instance bytes32 public immutable version; // ChainSlug for this deployed socket instance @@ -52,6 +52,8 @@ abstract contract SocketBase is SocketConfig { address appGateway_, address transmitter_, address target_, + uint256 value_, + uint256 deadline_, uint256 executionGasLimit_, bytes memory payload_ ) internal pure returns (bytes32) { @@ -62,6 +64,8 @@ abstract contract SocketBase is SocketConfig { appGateway_, transmitter_, target_, + value_, + deadline_, executionGasLimit_, payload_ ) diff --git a/contracts/protocol/utils/RescueFundsLib.sol b/contracts/protocol/utils/RescueFundsLib.sol index a2355bc6..a6319036 100644 --- a/contracts/protocol/utils/RescueFundsLib.sol +++ b/contracts/protocol/utils/RescueFundsLib.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.21; import "solmate/utils/SafeTransferLib.sol"; import "solmate/tokens/ERC20.sol"; -import {ZeroAddress} from "../utils/common/Errors.sol"; -import {ETH_ADDRESS} from "../utils/common/Constants.sol"; +import {ZeroAddress, InvalidTokenAddress} from "./common/Errors.sol"; +import {ETH_ADDRESS} from "./common/Constants.sol"; /** * @title RescueFundsLib @@ -12,11 +12,6 @@ import {ETH_ADDRESS} from "../utils/common/Constants.sol"; */ library RescueFundsLib { - /** - * @dev thrown when the given token address don't have any code - */ - error InvalidTokenAddress(); - /** * @dev Rescues funds from a contract. * @param token_ The address of the token contract. diff --git a/contracts/protocol/utils/common/Errors.sol b/contracts/protocol/utils/common/Errors.sol index 54724da0..1ab0cde6 100644 --- a/contracts/protocol/utils/common/Errors.sol +++ b/contracts/protocol/utils/common/Errors.sol @@ -26,4 +26,6 @@ error InvalidInboxCaller(); error PromisesNotResolved(); error InvalidPromise(); error InvalidIndex(); +error InvalidTransmitter(); error FeesNotSet(); +error InvalidTokenAddress(); \ No newline at end of file diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index cfb05a6b..72058e7c 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -18,6 +18,11 @@ enum Parallel { ON } +enum IsPlug { + YES, + NO +} + struct AppGatewayConfig { address plug; address appGateway; @@ -26,11 +31,13 @@ struct AppGatewayConfig { } struct AsyncRequest { + address finalizedBy; address appGateway; address transmitter; address target; address switchboard; uint256 executionGasLimit; + uint256 deadline; bytes32 asyncId; bytes32 root; bytes payload; @@ -53,13 +60,16 @@ struct CallFromInboxParams { } struct CallParams { + IsPlug isPlug; address asyncPromise; address target; uint32 chainSlug; CallType callType; Parallel isParallel; uint256 gasLimit; + uint256 value; bytes payload; + bytes initCallData; } struct DeployParams { @@ -67,13 +77,14 @@ struct DeployParams { bytes bytecode; } -struct ExecutePayloadParams { +struct AttestAndExecutePayloadParams { bytes32 payloadId; bytes32 root; address switchboard; address appGateway; address target; uint256 executionGasLimit; + uint256 deadline; bytes watcherSignature; bytes transmitterSignature; bytes payload; @@ -107,6 +118,7 @@ struct PayloadBatch { Fees fees; Bid winningBid; address[] lastBatchPromises; + bytes32[] lastBatchOfPayloads; bytes onCompleteData; } @@ -116,6 +128,7 @@ struct PayloadDetails { uint32 chainSlug; Parallel isParallel; CallType callType; + uint256 value; uint256 executionGasLimit; bytes payload; address[] next; @@ -126,8 +139,9 @@ struct PayloadRootParams { address transmitter; address target; bytes32 payloadId; + uint256 value; uint256 executionGasLimit; - uint256 expiryTime; + uint256 deadline; bytes payload; } diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index 02d8f720..cf3d0b73 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -6,7 +6,6 @@ import "../../interfaces/IAppGateway.sol"; import "../../interfaces/IPromise.sol"; import "../../interfaces/IFeesManager.sol"; import "solady/utils/Initializable.sol"; - import {PayloadRootParams, AsyncRequest, FinalizeParams, TimeoutRequest, CallFromInboxParams} from "../utils/common/Structs.sol"; import {TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidInboxCaller, ResolvingTimeoutTooEarly, CallFailed, AppGatewayAlreadyCalled} from "../utils/common/Errors.sol"; @@ -14,15 +13,14 @@ import {TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidInboxCaller, Resolv /// @notice Contract that handles payload verification, execution and app configurations contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { uint256 public maxTimeoutDelayInSeconds; - /// @notice Counter for tracking query requests - uint256 public queryCounter; - /// @notice Counter for tracking payload execution requests + /// @notice Counter for tracking payload requests uint256 public payloadCounter; - /// @notice Counter for tracking timeout requests - uint256 public timeoutCounter; /// @notice The expiry time for the payload uint256 public expiryTime; + /// @notice The chain slug of the watcher precompile + uint32 public vmChainSlug; + /// @notice Mapping to store async requests /// @dev payloadId => AsyncRequest struct mapping(bytes32 => AsyncRequest) public asyncRequests; @@ -37,14 +35,18 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { /// @dev callId => bool mapping(bytes32 => bool) public appGatewayCalled; - uint64 public version; - /// @notice Error thrown when an invalid chain slug is provided error InvalidChainSlug(); /// @notice Error thrown when an invalid app gateway reaches a plug error InvalidConnection(); /// @notice Error thrown if winning bid is assigned to an invalid transmitter error InvalidTransmitter(); + /// @notice Error thrown when a timeout request is invalid + error InvalidTimeoutRequest(); + /// @notice Error thrown when a payload id is invalid + error InvalidPayloadId(); + /// @notice Error thrown when a caller is invalid + error InvalidCaller(); event CalledAppGateway( bytes32 callId, @@ -99,19 +101,21 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { function initialize( address owner_, address addressResolver_, - uint256 defaultLimit_ + uint256 defaultLimit_, + uint256 expiryTime_, + uint32 vmChainSlug_ ) public reinitializer(1) { _setAddressResolver(addressResolver_); _initializeOwner(owner_); - version = 1; maxTimeoutDelayInSeconds = 24 * 60 * 60; // 24 hours - - LIMIT_DECIMALS = 18; + expiryTime = expiryTime_; // limit per day defaultLimit = defaultLimit_ * 10 ** LIMIT_DECIMALS; // limit per second defaultRatePerSecond = defaultLimit / (24 * 60 * 60); + + vmChainSlug = vmChainSlug_; } // ================== Timeout functions ================== @@ -129,7 +133,7 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { // from auction manager _consumeLimit(appGateway_, SCHEDULE, 1); uint256 executeAt = block.timestamp + delayInSeconds_; - bytes32 timeoutId = _encodeTimeoutId(timeoutCounter++); + bytes32 timeoutId = _encodeId(vmChainSlug, address(this)); timeoutRequests[timeoutId] = TimeoutRequest( timeoutId, msg.sender, @@ -147,12 +151,17 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { /// @dev Only callable by the contract owner function resolveTimeout(bytes32 timeoutId_) external onlyOwner { TimeoutRequest storage timeoutRequest_ = timeoutRequests[timeoutId_]; + + if (timeoutRequest_.target == address(0)) revert InvalidTimeoutRequest(); if (timeoutRequest_.isResolved) revert TimeoutAlreadyResolved(); if (block.timestamp < timeoutRequest_.executeAt) revert ResolvingTimeoutTooEarly(); + (bool success, ) = address(timeoutRequest_.target).call(timeoutRequest_.payload); if (!success) revert CallFailed(); + timeoutRequest_.isResolved = true; timeoutRequest_.executedAt = block.timestamp; + emit TimeoutResolved( timeoutId_, timeoutRequest_.target, @@ -168,9 +177,30 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { /// @return payloadId The unique identifier for the finalized request /// @return root The merkle root of the payload parameters function finalize( - FinalizeParams memory params_, - address originAppGateway_ + address originAppGateway_, + FinalizeParams memory params_ ) external returns (bytes32 payloadId, bytes32 root) { + // Generate a unique payload ID by combining chain, target, and counter + payloadId = _encodeWritePayloadId( + params_.payloadDetails.chainSlug, + params_.payloadDetails.target + ); + + root = _finalize(payloadId, originAppGateway_, params_); + } + + function refinalize(bytes32 payloadId_, FinalizeParams memory params_) external { + if (asyncRequests[payloadId_].appGateway == address(0)) revert InvalidPayloadId(); + if (asyncRequests[payloadId_].finalizedBy != msg.sender) revert InvalidCaller(); + + _finalize(payloadId_, asyncRequests[payloadId_].appGateway, params_); + } + + function _finalize( + bytes32 payloadId_, + address originAppGateway_, + FinalizeParams memory params_ + ) internal returns (bytes32 root) { if (params_.transmitter == address(0)) revert InvalidTransmitter(); // The app gateway is the caller of this function @@ -183,11 +213,10 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { params_.payloadDetails.appGateway ); - // Generate a unique payload ID by combining chain, target, and counter - payloadId = _encodePayloadId( + // Get the switchboard address from plug configurations + (, address switchboard) = getPlugConfigs( params_.payloadDetails.chainSlug, - params_.payloadDetails.target, - payloadCounter++ + params_.payloadDetails.target ); // Construct parameters for root calculation @@ -195,35 +224,32 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { params_.payloadDetails.appGateway, params_.transmitter, params_.payloadDetails.target, - payloadId, + payloadId_, + params_.payloadDetails.value, params_.payloadDetails.executionGasLimit, - expiryTime, + block.timestamp + expiryTime, params_.payloadDetails.payload ); // Calculate merkle root from payload parameters root = getRoot(rootParams_); - // Get the switchboard address from plug configurations - (, address switchboard) = getPlugConfigs( - params_.payloadDetails.chainSlug, - params_.payloadDetails.target - ); - // Create and store the async request with all necessary details AsyncRequest memory asyncRequest = AsyncRequest( + msg.sender, params_.payloadDetails.appGateway, params_.transmitter, params_.payloadDetails.target, switchboard, params_.payloadDetails.executionGasLimit, + block.timestamp + expiryTime, params_.asyncId, root, params_.payloadDetails.payload, params_.payloadDetails.next ); - asyncRequests[payloadId] = asyncRequest; - emit FinalizeRequested(payloadId, asyncRequest); + asyncRequests[payloadId_] = asyncRequest; + emit FinalizeRequested(payloadId_, asyncRequest); } // ================== Query functions ================== @@ -243,16 +269,18 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { // from payload delivery _consumeLimit(appGateway_, QUERY, 1); // Generate unique payload ID from query counter - payloadId = bytes32(queryCounter++); + payloadId = _encodeId(vmChainSlug, address(this)); // Create async request with minimal information for queries // Note: addresses set to 0 as they're not needed for queries AsyncRequest memory asyncRequest_ = AsyncRequest( + msg.sender, address(0), address(0), targetAddress_, address(0), 0, + block.timestamp + expiryTime, bytes32(0), bytes32(0), payload_, @@ -266,6 +294,8 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { /// @param payloadId_ The unique identifier of the request /// @param signature_ The watcher's signature /// @dev Only callable by the contract owner + /// @dev Watcher signs on following digest for validation on switchboard: + /// @dev keccak256(abi.encode(switchboard, root)) function finalized(bytes32 payloadId_, bytes calldata signature_) external onlyOwner { watcherSignatures[payloadId_] = signature_; emit Finalized(payloadId_, asyncRequests[payloadId_], signature_); @@ -283,6 +313,7 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { // Resolve each promise with its corresponding return data bool success; for (uint256 j = 0; j < next.length; j++) { + if (next[j] == address(0)) continue; success = IPromise(next[j]).markResolved( asyncRequest_.asyncId, resolvedPromises_[i].payloadId, @@ -310,6 +341,8 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { asyncRequest_.transmitter, asyncRequest_.appGateway ); + + // batch.isBatchCancelled } } @@ -323,6 +356,8 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { params_.appGateway, params_.transmitter, params_.target, + params_.value, + params_.deadline, params_.executionGasLimit, params_.payload ) @@ -377,30 +412,28 @@ contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { /// @notice Encodes a unique payload ID from chain slug, plug address, and counter /// @param chainSlug_ The identifier of the chain /// @param plug_ The plug address - /// @param counter_ The current counter value /// @return The encoded payload ID as bytes32 /// @dev Reverts if chainSlug is 0 - function _encodePayloadId( - uint32 chainSlug_, - address plug_, - uint256 counter_ - ) internal view returns (bytes32) { + function _encodeWritePayloadId(uint32 chainSlug_, address plug_) internal returns (bytes32) { if (chainSlug_ == 0) revert InvalidChainSlug(); (, address switchboard) = getPlugConfigs(chainSlug_, plug_); - // Encode payload ID by bit-shifting and combining: - // chainSlug (32 bits) | switchboard address (160 bits) | counter (64 bits) + return _encodeId(chainSlug_, switchboard); + } + function _encodeId( + uint32 chainSlug_, + address switchboardOrWatcher_ + ) internal returns (bytes32) { + // Encode payload ID by bit-shifting and combining: + // chainSlug (32 bits) | switchboard or watcher precompile address (160 bits) | counter (64 bits) return bytes32( - (uint256(chainSlug_) << 224) | (uint256(uint160(switchboard)) << 64) | counter_ + (uint256(chainSlug_) << 224) | + (uint256(uint160(switchboardOrWatcher_)) << 64) | + payloadCounter++ ); } - function _encodeTimeoutId(uint256 timeoutCounter_) internal view returns (bytes32) { - // watcher address (160 bits) | counter (64 bits) - return bytes32((uint256(uint160(address(this))) << 64) | timeoutCounter_); - } - function setExpiryTime(uint256 expiryTime_) external onlyOwner { expiryTime = expiryTime_; } diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index 8bc3b90f..e90f196a 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -11,14 +11,22 @@ abstract contract WatcherPrecompileConfig is WatcherPrecompileLimits { /// @dev chainSlug => plug => PlugConfig mapping(uint32 => mapping(address => PlugConfig)) internal _plugConfigs; - /// @notice Maps app gateway to their associated plugs per network - /// @dev appGateway => chainSlug => plug - mapping(address => mapping(uint32 => address)) public appGatewayPlugs; - /// @notice Maps chain slug to their associated switchboard /// @dev chainSlug => sb type => switchboard address mapping(uint32 => mapping(bytes32 => address)) public switchboards; + /// @notice Maps chain slug to their associated socket + /// @dev chainSlug => socket address + mapping(uint32 => address) public sockets; + + /// @notice Maps chain slug to their associated contract factory plug + /// @dev chainSlug => contract factory plug address + mapping(uint32 => address) public contractFactoryPlug; + + /// @notice Maps chain slug to their associated fees plug + /// @dev chainSlug => fees plug address + mapping(uint32 => address) public feesPlug; + // appGateway => chainSlug => plug => isValid mapping(address => mapping(uint32 => mapping(address => bool))) public isValidInboxCaller; @@ -34,6 +42,22 @@ abstract contract WatcherPrecompileConfig is WatcherPrecompileLimits { /// @param switchboard The address of the switchboard event SwitchboardSet(uint32 chainSlug, bytes32 sbType, address switchboard); + /// @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 + ); + /// @notice Configures app gateways with their respective plugs and switchboards /// @param configs_ Array of configurations containing app gateway, network, plug, and switchboard details /// @dev Only callable by the contract owner @@ -46,9 +70,6 @@ abstract contract WatcherPrecompileConfig is WatcherPrecompileLimits { switchboard: configs_[i].switchboard }); - // Create reverse mapping from app gateway to plug for easy lookup - appGatewayPlugs[configs_[i].appGateway][configs_[i].chainSlug] = configs_[i].plug; - emit PlugAdded(configs_[i].appGateway, configs_[i].chainSlug, configs_[i].plug); } } @@ -56,13 +77,27 @@ 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 setSwitchboard( + function setOnChainContracts( uint32 chainSlug_, bytes32 sbType_, - address switchboard_ - ) external onlyOwner { + address switchboard_, + address socket_, + address contractFactoryPlug_, + address feesPlug_ + ) external override onlyOwner { switchboards[chainSlug_][sbType_] = switchboard_; - emit SwitchboardSet(chainSlug_, sbType_, switchboard_); + sockets[chainSlug_] = socket_; + contractFactoryPlug[chainSlug_] = contractFactoryPlug_; + feesPlug[chainSlug_] = feesPlug_; + + emit OnChainContractSet( + chainSlug_, + sbType_, + switchboard_, + socket_, + contractFactoryPlug_, + feesPlug_ + ); } // @dev app gateway can set the valid plugs for each chain slug diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index 76a1bd55..df8f0f54 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -14,13 +14,13 @@ abstract contract WatcherPrecompileLimits is Ownable, IWatcherPrecompile { + /// @notice Number of decimals used in limit calculations + uint256 public constant LIMIT_DECIMALS = 18; + /// @notice Default limit value for any app gateway uint256 public defaultLimit; /// @notice Rate at which limit replenishes per second uint256 public defaultRatePerSecond; - /// @notice Number of decimals used in limit calculations - uint256 public LIMIT_DECIMALS; - // appGateway => limitType => receivingLimitParams mapping(address => mapping(bytes32 => LimitParams)) internal _limitParams; diff --git a/hardhat-scripts/constants/networks.ts b/hardhat-scripts/constants/networks.ts index f5be446d..8010747d 100644 --- a/hardhat-scripts/constants/networks.ts +++ b/hardhat-scripts/constants/networks.ts @@ -28,7 +28,6 @@ export const rpcKeys = (chainSlug: ChainSlug) => { return "EVMX_RPC"; } let chainName = chainSlugToHardhatChainName[chainSlug].toString(); - // console.log("chainName", chainName); chainName = chainName.replace("-", "_"); return `${chainName.toUpperCase()}_RPC`; }; diff --git a/hardhat-scripts/deploy/1.deploy.ts b/hardhat-scripts/deploy/1.deploy.ts index 40d8a2a6..ec95a47d 100644 --- a/hardhat-scripts/deploy/1.deploy.ts +++ b/hardhat-scripts/deploy/1.deploy.ts @@ -13,13 +13,12 @@ import { getProviderFromChainSlug } from "../constants"; import { ethers } from "hardhat"; import dev_addresses from "../../deployments/dev_addresses.json"; import { auctionEndDelaySeconds, chains } from "./config"; -import { - MAX_LIMIT, - EVMX_CHAIN_ID, - BID_TIMEOUT -} from "../constants/constants"; +import { MAX_LIMIT, EVMX_CHAIN_ID, BID_TIMEOUT } from "../constants/constants"; import { getImplementationAddress } from "./migration/migrate-proxies"; -import { CORE_CONTRACTS, EVMxCoreContracts } from "../constants/protocolConstants"; +import { + CORE_CONTRACTS, + EVMxCoreContracts, +} from "../constants/protocolConstants"; let offChainVMOwner: string; const main = async () => { @@ -212,11 +211,7 @@ const deployWatcherVMContracts = async () => { deployUtils = await deployContractWithProxy( EVMxCoreContracts.DeliveryHelper, `contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol`, - [ - addressResolver.address, - offChainVMOwner, - BID_TIMEOUT, - ], + [addressResolver.address, offChainVMOwner, BID_TIMEOUT], proxyFactory, deployUtils ); diff --git a/hardhat-scripts/deploy/2.roles.ts b/hardhat-scripts/deploy/2.roles.ts index d007ac94..35ed6836 100644 --- a/hardhat-scripts/deploy/2.roles.ts +++ b/hardhat-scripts/deploy/2.roles.ts @@ -40,7 +40,8 @@ export const main = async () => { ); for (const [contractName, roleHash] of Object.entries(REQUIRED_ROLES)) { - if (!chainAddresses[contractName as keyof ChainSocketAddresses]) continue; + if (!chainAddresses[contractName as keyof ChainSocketAddresses]) + continue; let contract = await getInstance( contractName as CORE_CONTRACTS, @@ -48,7 +49,10 @@ export const main = async () => { ); contract = contract.connect(signer); - const targetAddress = contractName === CORE_CONTRACTS.FastSwitchboard ? watcher : signer.address; + const targetAddress = + contractName === CORE_CONTRACTS.FastSwitchboard + ? watcher + : signer.address; const hasRole = await contract.callStatic["hasRole(bytes32,address)"]( getRoleHash(roleHash), @@ -65,7 +69,11 @@ export const main = async () => { } else { tx = await contract.grantRole(getRoleHash(roleHash), targetAddress); } - console.log(`granting ${roleHash} role to ${targetAddress} for ${contractName}`, chain, tx.hash); + console.log( + `granting ${roleHash} role to ${targetAddress} for ${contractName}`, + chain, + tx.hash + ); await tx.wait(); } } diff --git a/hardhat-scripts/deploy/3.upgradeManagers.ts b/hardhat-scripts/deploy/3.upgradeManagers.ts index 3f60c874..7f7a4a9f 100644 --- a/hardhat-scripts/deploy/3.upgradeManagers.ts +++ b/hardhat-scripts/deploy/3.upgradeManagers.ts @@ -48,11 +48,7 @@ export const main = async () => { socketContract ); - await setSwitchboard( - chainAddresses[CORE_CONTRACTS.FastSwitchboard], - chain, - addresses - ); + await setOnchainContracts(chain, addresses); await storeAddresses(chainAddresses, chain, DeploymentMode.DEV); } @@ -61,7 +57,7 @@ export const main = async () => { } }; -async function setSwitchboard(sbAddress, chain, addresses) { +async function setOnchainContracts(chain, addresses) { const providerInstance = getProviderFromChainSlug(EVMX_CHAIN_ID as ChainSlug); const signer: Wallet = new ethers.Wallet( process.env.WATCHER_PRIVATE_KEY as string, @@ -76,15 +72,37 @@ async function setSwitchboard(sbAddress, chain, addresses) { ).connect(signer); const fastSBtype = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("FAST")); - const currentValue = await watcherPrecompile.switchboards(chain, fastSBtype); - console.log({ current: currentValue, required: sbAddress }); + const sbAddress = addresses[chain][CORE_CONTRACTS.FastSwitchboard]; + const socketAddress = addresses[chain][CORE_CONTRACTS.Socket]; + const contractFactoryPlugAddress = + addresses[chain][EVMxCoreContracts.ContractFactoryPlug]; + const feesPlugAddress = addresses[chain][EVMxCoreContracts.FeesPlug]; - if (currentValue.toLowerCase() !== sbAddress.toLowerCase()) { + const currentValue = await watcherPrecompile.switchboards(chain, fastSBtype); + const currentSocket = await watcherPrecompile.sockets(chain); + const currentContractFactoryPlug = + await watcherPrecompile.contractFactoryPlug(chain); + const currentFeesPlug = await watcherPrecompile.feesPlug(chain); + + if ( + currentValue.toLowerCase() !== sbAddress.toLowerCase() || + currentSocket.toLowerCase() !== socketAddress.toLowerCase() || + currentContractFactoryPlug.toLowerCase() !== + contractFactoryPlugAddress.toLowerCase() || + currentFeesPlug.toLowerCase() !== feesPlugAddress.toLowerCase() + ) { const tx = await watcherPrecompile .connect(signer) - .setSwitchboard(chain, fastSBtype, sbAddress); + .setOnChainContracts( + chain, + fastSBtype, + sbAddress, + socketAddress, + contractFactoryPlugAddress, + feesPlugAddress + ); - console.log(`Setting sb for ${chain} to`, tx.hash); + console.log(`Setting onchain contracts for ${chain} to`, tx.hash); await tx.wait(); } } @@ -106,7 +124,7 @@ const registerSb = async (sbAddress, signer, socket) => { from: signer.address, }); - if (!sb) { + if (Number(sb) == 0) { const registerTx = await switchboard.registerSwitchboard(); console.log(`Registering Switchboard ${sbAddress}: ${registerTx.hash}`); await registerTx.wait(); diff --git a/hardhat-scripts/deploy/4.connect.ts b/hardhat-scripts/deploy/4.connect.ts index afc8706c..39e553cb 100644 --- a/hardhat-scripts/deploy/4.connect.ts +++ b/hardhat-scripts/deploy/4.connect.ts @@ -8,7 +8,10 @@ import { getInstance } from "./utils"; import { chains } from "./config"; import dev_addresses from "../../deployments/dev_addresses.json"; import { EVMX_CHAIN_ID } from "../constants/constants"; -import { CORE_CONTRACTS, EVMxCoreContracts } from "../constants/protocolConstants"; +import { + CORE_CONTRACTS, + EVMxCoreContracts, +} from "../constants/protocolConstants"; const plugs = [CORE_CONTRACTS.ContractFactoryPlug, CORE_CONTRACTS.FeesPlug]; export type AppGatewayConfig = { @@ -21,9 +24,7 @@ export type AppGatewayConfig = { export const getAppGateway = (plug: string, addresses: DeploymentAddresses) => { switch (plug) { case CORE_CONTRACTS.ContractFactoryPlug: - return addresses?.[EVMX_CHAIN_ID]?.[ - EVMxCoreContracts.DeliveryHelper - ]; + return addresses?.[EVMX_CHAIN_ID]?.[EVMxCoreContracts.DeliveryHelper]; case CORE_CONTRACTS.FeesPlug: return addresses?.[EVMX_CHAIN_ID]?.[EVMxCoreContracts.FeesManager]; default: @@ -52,9 +53,9 @@ export const isConfigSetOnSocket = async ( const plugConfigRegistered = await socket.getPlugConfig(plug.address); return ( plugConfigRegistered.appGateway.toLowerCase() === - appGateway?.toLowerCase() && + appGateway?.toLowerCase() && plugConfigRegistered.switchboard__.toLowerCase() === - switchboard.toLowerCase() + switchboard.toLowerCase() ); }; diff --git a/hardhat-scripts/utils/getEventTopics.ts b/hardhat-scripts/utils/getEventTopics.ts new file mode 100644 index 00000000..09339479 --- /dev/null +++ b/hardhat-scripts/utils/getEventTopics.ts @@ -0,0 +1,78 @@ +import fs from "fs"; +import path from "path"; +import { ethers } from "ethers"; + +// Function to recursively get all .sol files +function getSolFiles(dir: string, fileList: string[] = []): string[] { + const files = fs.readdirSync(dir); + + files.forEach((file) => { + const filePath = path.join(dir, file); + if (fs.statSync(filePath).isDirectory()) { + fileList = getSolFiles(filePath, fileList); + } else if (path.extname(file) === ".sol") { + fileList.push(filePath); + } + }); + + return fileList; +} + +// Function to extract event topics from contract file +function extractEventTopics(filePath: string): string[] { + const content = fs.readFileSync(filePath, "utf8"); + const eventRegex = /event\s+(\w+)\s*\(([\s\S]*?)\);/g; + const topics: string[] = []; + + let match; + while ((match = eventRegex.exec(content)) !== null) { + const eventName = match[1]; + const params = match[2] + .split(",") + .map((param) => param.trim().split(" ")[0]) // Extract only the type + .join(","); + const topic = ethers.utils.id(`${eventName}(${params})`); + topics.push(`${eventName} -> ${topic}`); + } + + return topics; +} + +// Main function +async function main() { + const contractsDir = path.join(__dirname, "../../contracts"); + const topicsDir = path.join(__dirname, "../../EventTopics.md"); + const solFiles = getSolFiles(contractsDir); + + console.log("Event Topics Found:"); + console.log("-------------------"); + + let mdContent = "# Event Topics\n\n"; + + for (const file of solFiles) { + const topics = extractEventTopics(file); + if (topics.length > 0) { + console.log(`\nIn ${path.relative(contractsDir, file)}:`); + mdContent += `\n## ${path.relative(contractsDir, file)}\n\n`; + mdContent += "| Event | Topic |\n|-------|-------|"; + + for (const topic of topics) { + console.log(topic); + const [eventName, topicHash] = topic.split(" -> "); + mdContent += `\n| \`${eventName}\` | \`${topicHash}\` |`; + } + mdContent += "\n"; + } + } + + // Write to EventTopics.md file + fs.writeFileSync(topicsDir, mdContent); + console.log("\nEvent topics have been written to EventTopics.md"); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/lib.tsconfig.json b/lib.tsconfig.json index c918562d..ba2f6b86 100644 --- a/lib.tsconfig.json +++ b/lib.tsconfig.json @@ -6,7 +6,10 @@ "esModuleInterop": true, "declaration": true, "resolveJsonModule": true, - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "node", + "allowJs": true }, - "include": ["./src", "./deployments"] + "include": ["src/**/*.ts", "lib/**/*.ts", "deployments/"], + "exclude": ["node_modules", "dist"] } diff --git a/script/CheckDepositedFees.s.sol b/script/CheckDepositedFees.s.sol new file mode 100644 index 00000000..70934724 --- /dev/null +++ b/script/CheckDepositedFees.s.sol @@ -0,0 +1,28 @@ +// 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/app-gateway/FeesManager.sol"; +import {Fees} from "../contracts/protocol/utils/common/Structs.sol"; +import {ETH_ADDRESS} from "../contracts/protocol/utils/common/Constants.sol"; + +contract CheckDepositedFees is Script { + function run() external { + vm.createSelectFork(vm.envString("EVMX_RPC")); + FeesManager feesManager = FeesManager(payable(vm.envAddress("FEES_MANAGER"))); + address appGateway = vm.envAddress("APP_GATEWAY"); + + (uint256 deposited, uint256 blocked) = feesManager.appGatewayFeeBalances( + appGateway, + 421614, + 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); + console.log("Available fees:", availableFees); + } +} diff --git a/script/counter-inbox/Increment.s.sol b/script/counter-inbox/Increment.s.sol index de4f6f45..37b56733 100644 --- a/script/counter-inbox/Increment.s.sol +++ b/script/counter-inbox/Increment.s.sol @@ -20,7 +20,6 @@ contract Increment is Script { address counterInbox = vm.envAddress("COUNTER_INBOX"); CounterInbox inbox = CounterInbox(counterInbox); - inbox.connectSocket(address(gateway), socket, switchboard); inbox.increaseOnGateway(100); vm.stopBroadcast(); diff --git a/script/mock/FinalizeAndExecution.s.sol b/script/mock/FinalizeAndExecution.s.sol index 3de980a6..628bd4aa 100644 --- a/script/mock/FinalizeAndExecution.s.sol +++ b/script/mock/FinalizeAndExecution.s.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; import {MockWatcherPrecompile} from "../../contracts/mock/MockWatcherPrecompile.sol"; -import {MockSocket} from "../../contracts/mock/MockSocket.sol"; +import "../../contracts/mock/MockSocket.sol"; import {CallType, FinalizeParams, PayloadDetails, Parallel} from "../../contracts/protocol/utils/common/Structs.sol"; contract InboxTest is Script { function run() external { @@ -28,6 +28,7 @@ contract InboxTest is Script { ), callType: CallType.WRITE, executionGasLimit: 1000000, + value: 0, next: new address[](0), isParallel: Parallel.OFF }); @@ -44,7 +45,15 @@ contract InboxTest is Script { vm.startBroadcast(arbDeployerPrivateKey); address socket = vm.envAddress("SOCKET"); MockSocket socketInstance = MockSocket(socket); - socketInstance.execute(payloadId, address(0), address(0), 10000000, bytes(""), bytes("")); + + ISocket.ExecuteParams memory executeParams = ISocket.ExecuteParams({ + payloadId: payloadId, + target: address(0), + executionGasLimit: 10000000, + deadline: 10000000, + payload: bytes("") + }); + socketInstance.execute(address(0), executeParams, bytes("")); vm.stopBroadcast(); } } diff --git a/test/DeliveryHelper.t.sol b/test/DeliveryHelper.t.sol index 13e27add..4702a879 100644 --- a/test/DeliveryHelper.t.sol +++ b/test/DeliveryHelper.t.sol @@ -112,23 +112,23 @@ contract DeliveryHelperTest is SetupTest { function connectDeliveryHelper() internal { vm.startPrank(owner); - arbConfig.contractFactoryPlug.connectSocket( + arbConfig.contractFactoryPlug.initSocket( address(deliveryHelper), address(arbConfig.socket), address(arbConfig.switchboard) ); - optConfig.contractFactoryPlug.connectSocket( + optConfig.contractFactoryPlug.initSocket( address(deliveryHelper), address(optConfig.socket), address(optConfig.switchboard) ); - arbConfig.feesPlug.connectSocket( + arbConfig.feesPlug.initSocket( address(feesManager), address(arbConfig.socket), address(arbConfig.switchboard) ); - optConfig.feesPlug.connectSocket( + optConfig.feesPlug.initSocket( address(feesManager), address(optConfig.socket), address(optConfig.switchboard) @@ -261,15 +261,13 @@ contract DeliveryHelperTest is SetupTest { function bidAndEndAuction(bytes32 asyncId) internal { placeBid(asyncId); - - bytes32 timeoutId = encodeTimeoutId(timeoutPayloadIdCounter++); - endAuction(timeoutId); + endAuction(); } function bidAndExecute(bytes32[] memory payloadIds, bytes32 asyncId_) internal { bidAndEndAuction(asyncId_); for (uint i = 0; i < payloadIds.length; i++) { - finalizeAndExecute(payloadIds[i], false); + finalizeAndExecute(payloadIds[i]); } } @@ -293,12 +291,13 @@ contract DeliveryHelperTest is SetupTest { address appGateway_ ) internal returns (bytes32 asyncId) { SocketContracts memory socketConfig = getSocketConfig(chainSlug_); + + asyncId = getCurrentAsyncId(); bytes32[] memory payloadIds = getWritePayloadIds( chainSlug_, address(socketConfig.switchboard), totalPayloads ); - asyncId = getCurrentAsyncId(); appDeployer_.deployContracts(chainSlug_); bidAndExecute(payloadIds, asyncId); @@ -318,12 +317,12 @@ contract DeliveryHelperTest is SetupTest { payloadIds[i * contractIds.length + j] = getWritePayloadId( chainSlugs_[i], address(getSocketConfig(chainSlugs_[i]).switchboard), - i * contractIds.length + j + writePayloadIdCounter + i * contractIds.length + j + payloadIdCounter ); } } // for fees - writePayloadIdCounter += chainSlugs_.length * contractIds.length + 1; + payloadIdCounter += chainSlugs_.length * contractIds.length + 1; appDeployer_.deployMultiChainContracts(chainSlugs_); bidAndExecute(payloadIds, asyncId); @@ -387,18 +386,15 @@ contract DeliveryHelperTest is SetupTest { uint32[] memory chainSlugs_ ) internal returns (bytes32 asyncId) { asyncId = getCurrentAsyncId(); - bidAndEndAuction(asyncId); for (uint i = 0; i < chainSlugs_.length; i++) { bytes32 payloadId = getWritePayloadId( chainSlugs_[i], address(getSocketConfig(chainSlugs_[i]).switchboard), - i + writePayloadIdCounter + payloadIdCounter++ ); - finalizeAndExecute(payloadId, false); + finalizeAndExecute(payloadId); } - - writePayloadIdCounter += chainSlugs_.length; } function createDeployPayloadDetail( @@ -409,8 +405,12 @@ contract DeliveryHelperTest is SetupTest { bytes32 salt = keccak256(abi.encode(appDeployer_, chainSlug_, deployCounter++)); bytes memory payload = abi.encodeWithSelector( IContractFactoryPlug.deployContract.selector, + true, + salt, + address(appDeployer_), + address(0), bytecode_, - salt + "" ); address asyncPromise = predictAsyncPromiseAddress( @@ -452,6 +452,7 @@ contract DeliveryHelperTest is SetupTest { payload: payload_, callType: callType_, executionGasLimit: executionGasLimit_, + value: 0, next: next_, isParallel: Parallel.ON }); @@ -471,10 +472,11 @@ contract DeliveryHelperTest is SetupTest { keccak256(abi.encode(address(auctionManager), vmChainSlug, asyncId, bidAmount, "")), transmitterPrivateKey ); + auctionManager.bid(asyncId, bidAmount, transmitterSignature, ""); } - function endAuction(bytes32 timeoutId) internal { + function endAuction() internal { // todo: // vm.expectEmit(true, false, false, true); // emit AuctionEnded( @@ -483,6 +485,8 @@ contract DeliveryHelperTest is SetupTest { // ); if (auctionEndDelaySeconds == 0) return; + bytes32 timeoutId = _encodeId(vmChainSlug, address(watcherPrecompile), payloadIdCounter++); + hoax(watcherEOA); watcherPrecompile.resolveTimeout(timeoutId); } @@ -492,14 +496,16 @@ contract DeliveryHelperTest is SetupTest { PayloadDetails memory payloadDetails ) internal view returns (bytes memory, bytes32) { SocketContracts memory socketConfig = getSocketConfig(payloadDetails.chainSlug); + (, , , , , , uint256 deadline, , , ) = watcherPrecompile.asyncRequests(payloadId); PayloadRootParams memory rootParams_ = PayloadRootParams( payloadDetails.appGateway, transmitterEOA, payloadDetails.target, payloadId, + payloadDetails.value, payloadDetails.executionGasLimit, - block.timestamp + 1000, + deadline, payloadDetails.payload ); bytes32 root = watcherPrecompile.getRoot(rootParams_); @@ -598,20 +604,10 @@ contract DeliveryHelperTest is SetupTest { resolvePromise(payloadId, returnData_); } - function finalizeAndExecute(bytes32 payloadId, bool isWithdraw) internal { + function finalizeAndExecute(bytes32 payloadId) internal { PayloadDetails memory payloadDetails = deliveryHelper.getPayloadDetails(payloadId); - finalizeAndExecute(payloadId, isWithdraw, payloadDetails); - } - - function finalizeAndExecute( - bytes32 payloadId, - bool isWithdraw, - PayloadDetails memory payloadDetails - ) internal { bytes memory returnData = finalizeAndRelay(payloadId, payloadDetails); - if (!isWithdraw) { - resolvePromise(payloadId, returnData); - } + resolvePromise(payloadId, returnData); } function finalizeAndRelay( @@ -651,17 +647,10 @@ contract DeliveryHelperTest is SetupTest { } function getCurrentAsyncId() public returns (bytes32) { + payloadIdCounter++; return bytes32((uint256(uint160(address(deliveryHelper))) << 64) | asyncCounterTest++); } - function getLatestAsyncId() public view returns (bytes32) { - return bytes32((uint256(uint160(address(deliveryHelper))) << 64) | asyncCounterTest); - } - - function getTimeoutPayloadId(uint256 counter_) internal view returns (bytes32) { - return bytes32((uint256(uint160(address(deliveryHelper))) << 64) | counter_); - } - function getOnChainAndForwarderAddresses( uint32 chainSlug_, bytes32 contractId_, diff --git a/test/FeesTest.t.sol b/test/FeesTest.t.sol index 98035397..67d053a0 100644 --- a/test/FeesTest.t.sol +++ b/test/FeesTest.t.sol @@ -68,9 +68,9 @@ contract FeesTest is DeliveryHelperTest { hoax(transmitterEOA); (bytes32 payloadId, , PayloadDetails memory payloadDetails) = feesManager .withdrawTransmitterFees(feesChainSlug, ETH_ADDRESS, address(receiver)); - writePayloadIdCounter++; + payloadIdCounter++; + finalizeAndRelay(payloadId, payloadDetails); - finalizeAndExecute(payloadId, true, payloadDetails); assertEq( transmitterReceiverBalanceBefore + bidAmount, address(receiver).balance, @@ -92,6 +92,7 @@ contract FeesTest is DeliveryHelperTest { uint256 receiverBalanceBefore = receiver.balance; uint256 withdrawAmount = 0.5 ether; + counterGateway.withdrawFeeTokens(feesChainSlug, ETH_ADDRESS, withdrawAmount, receiver); asyncId = getCurrentAsyncId(); @@ -101,7 +102,9 @@ contract FeesTest is DeliveryHelperTest { 1 ); bidAndEndAuction(asyncId); - finalizeAndExecute(payloadIds[0], true); + + PayloadDetails memory payloadDetails = deliveryHelper.getPayloadDetails(payloadIds[0]); + finalizeAndRelay(payloadIds[0], payloadDetails); assertEq( depositAmount - withdrawAmount, address(feesConfig.feesPlug).balance, diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index b9de3de5..b407e85e 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -27,7 +27,7 @@ contract InboxTest is DeliveryHelperTest { ); // Connect the inbox to the gateway and socket - inbox.connectSocket( + inbox.initSocket( address(gateway), address(arbConfig.socket), address(arbConfig.switchboard) diff --git a/test/MockWatcherPrecompileImpl.sol b/test/MockWatcherPrecompileImpl.sol index 3ed0b3cc..5e92693a 100644 --- a/test/MockWatcherPrecompileImpl.sol +++ b/test/MockWatcherPrecompileImpl.sol @@ -14,8 +14,6 @@ contract MockWatcherPrecompileImpl is WatcherPrecompile { _initializeOwner(owner_); maxTimeoutDelayInSeconds = 24 * 60 * 60; // 24 hours - LIMIT_DECIMALS = 18; - // limit per day defaultLimit = defaultLimit_ * 10 ** LIMIT_DECIMALS; // limit per second diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index f7566fe3..513c7ed0 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -33,11 +33,9 @@ contract SetupTest is Test { uint32 arbChainSlug = 421614; uint32 optChainSlug = 11155420; uint32 vmChainSlug = 1; + uint256 expiryTime = 10000000; - uint256 public writePayloadIdCounter = 0; - uint256 public readPayloadIdCounter = 0; - uint256 public timeoutPayloadIdCounter = 0; - + uint256 public payloadIdCounter = 0; uint256 public defaultLimit = 1000; bytes public asyncPromiseBytecode = type(AsyncPromise).creationCode; @@ -82,7 +80,14 @@ contract SetupTest is Test { vm.stopPrank(); hoax(watcherEOA); - watcherPrecompile.setSwitchboard(chainSlug_, FAST, address(switchboard)); + watcherPrecompile.setOnChainContracts( + chainSlug_, + FAST, + address(switchboard), + address(socket), + address(contractFactoryPlug), + address(feesPlug) + ); return SocketContracts({ @@ -118,7 +123,9 @@ contract SetupTest is Test { WatcherPrecompile.initialize.selector, watcherEOA, address(addressResolverProxy), - defaultLimit + defaultLimit, + expiryTime, + vmChainSlug ); vm.expectEmit(true, true, true, false); emit Initialized(version); @@ -171,8 +178,10 @@ contract SetupTest is Test { bytes32 transmitterDigest = keccak256(abi.encode(address(socketConfig.socket), payloadId)); bytes memory transmitterSig = _createSignature(transmitterDigest, transmitterPrivateKey); + (, , , , , , uint256 deadline, , , ) = watcherPrecompile.asyncRequests(payloadId); + vm.startPrank(transmitterEOA); - ExecutePayloadParams memory params = ExecutePayloadParams({ + AttestAndExecutePayloadParams memory params = AttestAndExecutePayloadParams({ switchboard: address(socketConfig.switchboard), root: root, watcherSignature: watcherSignature, @@ -181,7 +190,8 @@ contract SetupTest is Test { executionGasLimit: payloadDetails.executionGasLimit, transmitterSignature: transmitterSig, payload: payloadDetails.payload, - target: payloadDetails.target + target: payloadDetails.target, + deadline: deadline }); bytes memory returnData = socketConfig.socketBatcher.attestAndExecute(params); @@ -211,10 +221,7 @@ contract SetupTest is Test { address switchboard_, uint256 counter_ ) internal pure returns (bytes32) { - return - bytes32( - (uint256(chainSlug_) << 224) | (uint256(uint160(switchboard_)) << 64) | counter_ - ); + return _encodeId(chainSlug_, switchboard_, counter_); } function getWritePayloadIds( @@ -224,15 +231,19 @@ contract SetupTest is Test { ) internal returns (bytes32[] memory) { bytes32[] memory payloadIds = new bytes32[](numPayloads); for (uint256 i = 0; i < numPayloads; i++) { - payloadIds[i] = getWritePayloadId(chainSlug_, switchboard_, i + writePayloadIdCounter); + payloadIds[i] = _encodeId(chainSlug_, switchboard_, payloadIdCounter++); } - - writePayloadIdCounter += numPayloads; return payloadIds; } - function encodeTimeoutId(uint256 timeoutCounter_) internal view returns (bytes32) { - // watcher address (160 bits) | counter (64 bits) - return bytes32((uint256(uint160(address(watcherPrecompile))) << 64) | timeoutCounter_); + function _encodeId( + uint32 chainSlug_, + address sbOrWatcher_, + uint256 counter_ + ) internal pure returns (bytes32) { + return + bytes32( + (uint256(chainSlug_) << 224) | (uint256(uint160(sbOrWatcher_)) << 64) | counter_ + ); } } diff --git a/test/apps/Counter.t.sol b/test/apps/Counter.t.sol index c35c471e..e0ed658b 100644 --- a/test/apps/Counter.t.sol +++ b/test/apps/Counter.t.sol @@ -69,7 +69,7 @@ contract CounterTest is DeliveryHelperTest { ); } - function testCounterIncrement() external { + function testCounterIncrement1() external { deploySetup(); deployCounterApp(arbChainSlug); @@ -117,12 +117,14 @@ contract CounterTest is DeliveryHelperTest { chains[0] = arbChainSlug; chains[1] = optChainSlug; _executeWriteBatchMultiChain(chains); + assertEq(Counter(arbCounter).counter(), arbCounterBefore + 1); assertEq(Counter(optCounter).counter(), optCounterBefore + 1); } function testCounterReadMultipleChains() external { testCounterIncrementMultipleChains(); + (address arbCounter, address arbCounterForwarder) = getOnChainAndForwarderAddresses( arbChainSlug, counterId, @@ -137,22 +139,24 @@ contract CounterTest is DeliveryHelperTest { address[] memory instances = new address[](2); instances[0] = arbCounterForwarder; instances[1] = optCounterForwarder; + + bytes32 bridgeAsyncId = getCurrentAsyncId(); + bytes32[] memory payloadIds = new bytes32[](3); - payloadIds[0] = bytes32(readPayloadIdCounter++); - payloadIds[1] = bytes32(readPayloadIdCounter++); + payloadIds[0] = _encodeId(vmChainSlug, address(watcherPrecompile), payloadIdCounter++); + payloadIds[1] = _encodeId(vmChainSlug, address(watcherPrecompile), payloadIdCounter++); payloadIds[2] = getWritePayloadId( arbChainSlug, address(getSocketConfig(arbChainSlug).switchboard), - writePayloadIdCounter++ + payloadIdCounter++ ); - bytes32 bridgeAsyncId = getCurrentAsyncId(); counterGateway.readCounters(instances); - finalizeQuery(payloadIds[0], abi.encode(Counter(arbCounter).counter())); - finalizeQuery(payloadIds[1], abi.encode(Counter(optCounter).counter())); bidAndEndAuction(bridgeAsyncId); - finalizeAndExecute(payloadIds[2], false); + finalizeQuery(payloadIds[0], abi.encode(Counter(arbCounter).counter())); + finalizeQuery(payloadIds[1], abi.encode(Counter(optCounter).counter())); + finalizeAndExecute(payloadIds[2]); } } diff --git a/test/apps/SuperTokenLockable.t.sol b/test/apps/SuperTokenLockable.t.sol index f832a162..95e6bebe 100644 --- a/test/apps/SuperTokenLockable.t.sol +++ b/test/apps/SuperTokenLockable.t.sol @@ -277,27 +277,26 @@ contract SuperTokenLockableTest is DeliveryHelperTest { }); uint32 srcChainSlug = IForwarder(userOrder.srcToken).getChainSlug(); uint32 dstChainSlug = IForwarder(userOrder.dstToken).getChainSlug(); + bytes32 bridgeAsyncId = getCurrentAsyncId(); bytes32[] memory payloadIds = new bytes32[](4); payloadIds[0] = getWritePayloadId( srcChainSlug, address(getSocketConfig(srcChainSlug).switchboard), - writePayloadIdCounter++ + payloadIdCounter++ ); - payloadIds[1] = bytes32(readPayloadIdCounter++); + payloadIds[1] = _encodeId(vmChainSlug, address(watcherPrecompile), payloadIdCounter++); payloadIds[2] = getWritePayloadId( dstChainSlug, address(getSocketConfig(dstChainSlug).switchboard), - writePayloadIdCounter++ + payloadIdCounter++ ); payloadIds[3] = getWritePayloadId( srcChainSlug, address(getSocketConfig(srcChainSlug).switchboard), - writePayloadIdCounter++ + payloadIdCounter++ ); - writePayloadIdCounter++; - - bytes32 bridgeAsyncId = getCurrentAsyncId(); + payloadIdCounter++; bytes memory encodedOrder = abi.encode(userOrder); appContracts.superTokenLockableApp.bridge(encodedOrder); @@ -312,18 +311,20 @@ contract SuperTokenLockableTest is DeliveryHelperTest { bridgeAsyncId, 0 ); - finalizeAndExecute(payloadIds[0], false); + finalizeAndExecute(payloadIds[0]); payloadDetails = deliveryHelper.getPayloadIndexDetails(bridgeAsyncId, 2); vm.expectEmit(true, false, false, false); emit FinalizeRequested( payloadIds[2], AsyncRequest( + address(deliveryHelper), address(0), transmitterEOA, payloadDetails.target, address(0), payloadDetails.executionGasLimit, + 0, bridgeAsyncId, bytes32(0), payloadDetails.payload, @@ -331,20 +332,21 @@ contract SuperTokenLockableTest is DeliveryHelperTest { ) ); finalizeQuery(payloadIds[1], abi.encode(srcAmount)); - finalizeAndExecute(payloadIds[2], false); + finalizeAndExecute(payloadIds[2]); payloadDetails = deliveryHelper.getPayloadIndexDetails(bridgeAsyncId, 3); - finalizeAndExecute(payloadIds[3], false); + finalizeAndExecute(payloadIds[3]); } function testCancel() public { (bytes32 bridgeAsyncId, bytes32[] memory payloadIds) = _bridge(); - finalizeAndExecute(payloadIds[0], false); + finalizeAndExecute(payloadIds[0]); vm.expectEmit(true, true, false, true); emit BatchCancelled(bridgeAsyncId); finalizeQuery(payloadIds[1], abi.encode(0.001 ether)); + bytes32 cancelAsyncId = getCurrentAsyncId(); bytes32[] memory cancelPayloadIds = new bytes32[](1); uint32 srcChainSlug = IForwarder(userOrder.srcToken).getChainSlug(); @@ -352,11 +354,9 @@ contract SuperTokenLockableTest is DeliveryHelperTest { cancelPayloadIds[0] = getWritePayloadId( srcChainSlug, address(getSocketConfig(srcChainSlug).switchboard), - writePayloadIdCounter++ + payloadIdCounter++ ); - bytes32 cancelAsyncId = getCurrentAsyncId(); - // bidAndEndAuction(cancelAsyncId); // finalizeAndExecute( // cancelPayloadIds[0], @@ -538,20 +538,20 @@ contract SuperTokenLockableTest is DeliveryHelperTest { // payloadIds[0] = getWritePayloadId( // srcChainSlug, // address(getSocketConfig(srcChainSlug).contractFactoryPlug), - // writePayloadIdCounter++ + // payloadIdCounter++ // ); - // payloadIds[1] = bytes32(readPayloadIdCounter++); + // payloadIds[1] = _encodeId(vmChainSlug, address(watcherPrecompile), payloadIdCounter++); // payloadIds[2] = getWritePayloadId( // dstChainSlug, // address(getSocketConfig(dstChainSlug).contractFactoryPlug), - // writePayloadIdCounter++ + // payloadIdCounter++ // ); // payloadIds[3] = getWritePayloadId( // srcChainSlug, // address(getSocketConfig(srcChainSlug).contractFactoryPlug), - // writePayloadIdCounter++ + // payloadIdCounter++ // ); - // writePayloadIdCounter++; + // payloadIdCounter++; // PayloadDetails[] // memory payloadDetails = createBridgePayloadDetailsArray( @@ -635,7 +635,7 @@ contract SuperTokenLockableTest is DeliveryHelperTest { // cancelPayloadIds[0] = getWritePayloadId( // srcChainSlug, // address(getSocketConfig(srcChainSlug).contractFactoryPlug), - // writePayloadIdCounter++ + // payloadIdCounter++ // ); // PayloadDetails[] diff --git a/testScript.sh b/testScript.sh index 90929365..1157c8a0 100644 --- a/testScript.sh +++ b/testScript.sh @@ -8,7 +8,7 @@ source .env && forge script script/parallel-counter/checkCounters.s.sol --broadc ## Counter -source .env && forge script script/counter/DeployCounterOffchain.s.sol --broadcast --skip-simulation +source .env && forge script script/counter/deployEVMxCounterApp.s.sol --broadcast --skip-simulation source .env && forge script script/counter/DeployCounterOnchain.s.sol --broadcast --skip-simulation ## set limits for the app gateway using API source .env && cast send $DEPLOYER "deployContracts(uint32)" 421614 --private-key $PRIVATE_KEY @@ -53,4 +53,5 @@ source .env && forge script script/admin/UpdateLimits.s.sol --broadcast --skip-s # add fees -source .env && forge script script/DepositFees.s.sol --broadcast --skip-simulation +source .env && forge script script/PayFeesInArbitrumETH.s.sol --broadcast --skip-simulation +source .env && forge script script/CheckDepositedFees.s.sol