diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3c6073ba6..0093edcd0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -76,7 +76,7 @@ jobs: yarn- - name: Install Yarn dependencies - run: yarn install + run: yarn install - name: Set up Python uses: actions/setup-python@v4 @@ -89,15 +89,15 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt - - name: Install Cairo - run: curl -L https://github.com/starkware-libs/cairo/releases/download/v2.2.0/release-x86_64-unknown-linux-musl.tar.gz > cairo.tar.gz + - name: Install Cairo + run: curl -L https://github.com/starkware-libs/cairo/releases/download/v2.2.0/release-x86_64-unknown-linux-musl.tar.gz > cairo.tar.gz - name: Extract Cairo run: tar -xvf cairo.tar.gz - - name: Install Scarb + - name: Install Scarb uses: software-mansion/setup-scarb@v1 - with: + with: scarb-version: 0.7.0 - name: Check Cairo formatting @@ -106,7 +106,7 @@ jobs: - name: Build Cairo contracts working-directory: ./starknet - run: scarb build --verbose + run: scarb build --verbose - name: Run Cairo tests working-directory: ./starknet @@ -116,4 +116,4 @@ jobs: run: yarn hardhat starknet-build - name: run Hardhat tests - run: yarn test:l1-execution; yarn test:eth-sig-auth; yarn test:stark-sig-auth; yarn test:eth-tx-auth \ No newline at end of file + run: yarn test:l1-execution; yarn test:eth-sig-auth; yarn test:stark-sig-auth; yarn test:eth-tx-auth diff --git a/ethereum/src/execution-strategies/L1AvatarExecutionStrategy.sol b/ethereum/src/execution-strategies/L1AvatarExecutionStrategy.sol index 1f0367384..7f0380329 100644 --- a/ethereum/src/execution-strategies/L1AvatarExecutionStrategy.sol +++ b/ethereum/src/execution-strategies/L1AvatarExecutionStrategy.sol @@ -29,6 +29,9 @@ contract L1AvatarExecutionStrategy is SimpleQuorumExecutionStrategy { /// @dev Emitted each time the Execution Relayer is set. event ExecutionRelayerSet(uint256 indexed newExecutionRelayer); + /// @dev Emitted each time a proposal is executed. + event ProposalExecuted(uint256 indexed space, bytes32 executionHash); + /// @notice Emitted when a new Avatar Execution Strategy is initialized. /// @param _owner Address of the owner of the strategy. /// @param _target Address of the avatar that this module will pass transactions to. @@ -135,6 +138,7 @@ contract L1AvatarExecutionStrategy is SimpleQuorumExecutionStrategy { if (bytes32(executionHash) != keccak256(abi.encode(transactions))) revert InvalidPayload(); _execute(transactions); + emit ProposalExecuted(space, bytes32(executionHash)); } /// @dev Reverts if the expected message was not received from L2. diff --git a/ethereum/src/mocks/L1AvatarExecutionStrategyMockMessaging.sol b/ethereum/src/mocks/L1AvatarExecutionStrategyMockMessaging.sol index 770fd226b..d2608988a 100644 --- a/ethereum/src/mocks/L1AvatarExecutionStrategyMockMessaging.sol +++ b/ethereum/src/mocks/L1AvatarExecutionStrategyMockMessaging.sol @@ -7,25 +7,31 @@ import "./MockStarknetMessaging.sol"; import {SimpleQuorumExecutionStrategy} from "../execution-strategies/SimpleQuorumExecutionStrategy.sol"; import "../types.sol"; -/// @title L1 Avatar Execution Strategy +/// @title L1 Avatar Execution Strategy (Mock) /// @notice Used to execute SX Starknet proposal transactions from an Avatar contract on Ethereum. /// @dev An Avatar contract is any contract that implements the IAvatar interface, eg a Gnosis Safe. contract L1AvatarExecutionStrategyMockMessaging is SimpleQuorumExecutionStrategy { - /// @dev Address of the avatar that this module will pass transactions to. + /// @notice Address of the avatar that this module will pass transactions to. address public target; - /// The Starknet Core contract. + /// @notice Address of the Starknet Core contract. address public starknetCore; - /// Address of the StarkNet contract that will send execution details to this contract in a L2 -> L1 message + /// Address of the Starknet contract that will send execution details to this contract in a L2 -> L1 message. uint256 public executionRelayer; /// @dev Emitted each time the Target is set. event TargetSet(address indexed newTarget); + /// @dev Emitted each time the Starknet Core is set. + event StarknetCoreSet(address indexed newStarknetCore); + /// @dev Emitted each time the Execution Relayer is set. event ExecutionRelayerSet(uint256 indexed newExecutionRelayer); + /// @dev Emitted each time a proposal is executed. + event ProposalExecuted(uint256 indexed space, bytes32 executionHash); + /// @notice Emitted when a new Avatar Execution Strategy is initialized. /// @param _owner Address of the owner of the strategy. /// @param _target Address of the avatar that this module will pass transactions to. @@ -83,13 +89,6 @@ contract L1AvatarExecutionStrategyMockMessaging is SimpleQuorumExecutionStrategy emit L1AvatarExecutionStrategySetUp(_owner, _target, _starknetCore, _executionRelayer, _starknetSpaces, _quorum); } - /// @notice Sets the Starknet execution relayer contract - /// @param _executionRelayer Address of the new execution relayer contract - function setExecutionRelayer(uint256 _executionRelayer) external onlyOwner { - executionRelayer = _executionRelayer; - emit ExecutionRelayerSet(_executionRelayer); - } - /// @notice Sets the target address /// @param _target Address of the avatar that this module will pass transactions to. function setTarget(address _target) external onlyOwner { @@ -97,6 +96,20 @@ contract L1AvatarExecutionStrategyMockMessaging is SimpleQuorumExecutionStrategy emit TargetSet(_target); } + /// @notice Sets the Starknet Core contract + /// @param _starknetCore Address of the new Starknet Core contract. + function setStarknetCore(address _starknetCore) external onlyOwner { + starknetCore = _starknetCore; + emit StarknetCoreSet(_starknetCore); + } + + /// @notice Sets the Starknet execution relayer contract + /// @param _executionRelayer Address of the new execution relayer contract + function setExecutionRelayer(uint256 _executionRelayer) external onlyOwner { + executionRelayer = _executionRelayer; + emit ExecutionRelayerSet(_executionRelayer); + } + /// @notice Executes a proposal /// @param space The address of the space that the proposal was created in. /// @param proposal The proposal struct. @@ -125,6 +138,7 @@ contract L1AvatarExecutionStrategyMockMessaging is SimpleQuorumExecutionStrategy if (bytes32(executionHash) != keccak256(abi.encode(transactions))) revert InvalidPayload(); _execute(transactions); + emit ProposalExecuted(space, bytes32(executionHash)); } /// @dev Reverts if the expected message was not received from L2. diff --git a/tests/l1-avatar-execution.test.ts b/tests/l1-avatar-execution.test.ts index f4d2ff778..5b221cd72 100644 --- a/tests/l1-avatar-execution.test.ts +++ b/tests/l1-avatar-execution.test.ts @@ -54,7 +54,7 @@ describe('L1 Avatar Execution', function () { await account.declare(vanillaProposalValidationStrategyFactory); await account.declare(ethRelayerFactory); await account.declare(spaceFactory); - } catch {} + } catch { } starkTxAuthenticator = await account.deploy(starkTxAuthenticatorFactory); vanillaVotingStrategy = await account.deploy(vanillaVotingStrategyFactory); @@ -186,7 +186,7 @@ describe('L1 Avatar Execution', function () { // Proposal data can either be extracted from the message sent to L1 (as done here) or pulled from the contract directly const [proposal, forVotes, againstVotes, abstainVotes] = extractMessagePayload(message_payload); - await l1AvatarExecutionStrategy.execute( + await expect(l1AvatarExecutionStrategy.execute( space.address, proposal, forVotes, @@ -194,7 +194,7 @@ describe('L1 Avatar Execution', function () { abstainVotes, executionHash, [proposalTx], - ); + )).to.emit(l1AvatarExecutionStrategy, 'ProposalExecuted').withArgs(space.address.toString(), executionHash); }, 10000000); it('should execute a proposal with multiple txs via the Avatar Execution Strategy connected to a Safe', async function () {