Skip to content

Commit

Permalink
Merge branch 'dev' of github.com:defi-wonderland/safe-liveness into f…
Browse files Browse the repository at this point in the history
…eat/needs-update-guard
  • Loading branch information
0xOneTony committed Nov 20, 2023
2 parents b106de5 + 3e911f7 commit cd3eb25
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 89 deletions.
114 changes: 25 additions & 89 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,107 +1,43 @@
<img src="https://raw.githubusercontent.com/defi-wonderland/brand/v1.0.0/external/solidity-foundry-boilerplate-banner.png" alt="wonderland banner" align="center" />
<br />
# Safe Liveness

<div align="center"><strong>Start your next Solidity project with Foundry in seconds</strong></div>
<div align="center">A highly scalable foundation focused on DX and best practices</div>
⚠️ The code has not been audited yet, tread with caution.

<br />
## Overview

## Features
Safe-Liveness is a module that will tackle the liveness problem, one of the main challenges faced by smart wallets to improve cross-chain user experience.

<dl>
<dt>Sample contracts</dt>
<dd>Basic Greeter contract with an external interface.</dd>
Unlike EOAs, smart wallets have configuration settings, which can cause synchronization problems across chains. Consequently, SAFEs on different chains function as separate contracts, even though they may share the same address and configuration parameters during deployment. This problem becomes critical when there’s a change in the owners’ list.

<dt>Foundry setup</dt>
<dd>Foundry configuration with multiple custom profiles and remappings.</dd>

<dt>Deployment scripts</dt>
<dd>Sample scripts to deploy contracts on both mainnet and testnet.</dd>

<dt>Sample e2e & unit tests</dt>
<dd>Example tests showcasing mocking, assertions and configuration for mainnet forking. As well it includes everything needed in order to check code coverage.</dd>

<dt>Linter</dt>
<dd>Simple and fast solidity linting thanks to forge fmt</a>.</dd>

<dt>Github workflows CI</dt>
<dd>Run all tests and see the coverage as you push your changes.</dd>
</dl>
We will create a module that can verify Safe ownership based on a storage proof, allowing you to easily broadcast any changes in your Safe to other chains.

## Setup

1. Install Foundry by following the instructions from [their repository](https://github.com/foundry-rs/foundry#installation).
2. Copy the `.env.example` file to `.env` and fill in the variables.
3. Install the dependencies by running: `yarn install`. In case there is an error with the commands, run `foundryup` and try them again.
This project uses [Foundry](https://book.getfoundry.sh/). To build it locally, run:

## Build

The default way to build the code is suboptimal but fast, you can run it via:

```bash
```sh
git clone [email protected]:defi-wonderland/safe-liveness.git
cd safe-liveness
yarn install
yarn build
```

In order to build a more optimized code ([via IR](https://docs.soliditylang.org/en/v0.8.15/ir-breaking-changes.html#solidity-ir-based-codegen-changes)), run:

```bash
yarn build:optimized
```

## Running tests

Unit tests should be isolated from any externalities, while E2E usually run in a fork of the blockchain. In this boilerplate you will find example of both.

In order to run both unit and E2E tests, run:

```bash
yarn test
```

In order to just run unit tests, run:
### Available Commands

```bash
yarn test:unit
```
Make sure to set `MAINNET_RPC` environment variable before running end-to-end tests.

In order to run unit tests and run way more fuzzing than usual (5x), run:
| Yarn Command | Description |
| ----------------------- | ---------------------------------------------------------- |
| `yarn build` | Compile all contracts. |
| `yarn coverage` | See `forge coverage` report. |
| `yarn deploy` | Deploy the contracts to Mainnet. |
| `yarn test` | Run all unit and e2e tests. |
| `yarn test:unit` | Run unit tests. |
| `yarn test:e2e` | Run e2e tests. |

```bash
yarn test:unit:deep
```
## Contributors

In order to just run e2e tests, run:

```bash
yarn test:e2e
```

In order to check your current code coverage, run:

```bash
yarn coverage
```

<br>

## Deploy & verify

### Setup

Configure the `.env` variables.

### Goerli

```bash
yarn deploy:goerli
```

### Mainnet

```bash
yarn deploy:mainnet
```
Safe-Liveness was built with ❤️ by [Wonderland](https://defi.sucks).

The deployments are stored in ./broadcast
Wonderland is a team of top Web3 researchers, developers, and operators who believe that the future needs to be open-source, permissionless, and decentralized.

See the [Foundry Book for available options](https://book.getfoundry.sh/reference/forge/forge-create.html).
[DeFi sucks](https://defi.sucks), but Wonderland is here to make it better.
46 changes: 46 additions & 0 deletions solidity/contracts/BlockHeaderOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.19;

/**
* @title BlockHeaderOracle
* @notice This contract's purpose is to return the latest stored L1 block header and timestamp
* @notice Every X minutes a "magical" off-chain agent provides the latest block header and timestamp
*/
contract BlockHeaderOracle {
/**
* @notice Emits when the block header and timestamp are updated
*/
event BlockHeaderUpdated(bytes _blockHeader, uint256 _blockTimestamp, uint256 _blockNumber);

/**
* @notice The block header
*/
bytes public blockHeader;

/**
* @notice The block timestamp of the latest block header
*/
uint256 public blockTimestamp;

/**
* @notice Updates the block header and timestamp
* @param _blockHeader The block header
* @param _blockTimestamp The block timestamp
* @param _blockNumber The block number
*/
function updateBlockHeader(bytes memory _blockHeader, uint256 _blockTimestamp, uint256 _blockNumber) external {
blockHeader = _blockHeader;
blockTimestamp = _blockTimestamp;

emit BlockHeaderUpdated(_blockHeader, _blockTimestamp, _blockNumber);
}

/**
* @notice Returns the latest block header and timestamp
* @return _blockHeader The block header
* @return _blockTimestamp The block timestamp
*/
function getLatestBlockHeader() external view returns (bytes memory _blockHeader, uint256 _blockTimestamp) {
return (blockHeader, blockTimestamp);
}
}
9 changes: 9 additions & 0 deletions solidity/test/e2e/Common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import {DSTestPlus} from '@defi-wonderland/solidity-utils/solidity/test/DSTestPl
import {IERC20} from 'isolmate/interfaces/tokens/IERC20.sol';
import {SafeProxy} from 'safe-contracts/proxies/SafeProxy.sol';
import {Enum} from 'safe-contracts/common/Enum.sol';

import {StorageMirror} from 'contracts/StorageMirror.sol';
import {UpdateStorageMirrorGuard} from 'contracts/UpdateStorageMirrorGuard.sol';
import {GuardCallbackModule} from 'contracts/GuardCallbackModule.sol';
import {BlockHeaderOracle} from 'contracts/BlockHeaderOracle.sol';

import {IGuardCallbackModule} from 'interfaces/IGuardCallbackModule.sol';
import {ISafe} from 'interfaces/ISafe.sol';

import {IGnosisSafeProxyFactory} from 'test/e2e/IGnosisSafeProxyFactory.sol';
import {TestConstants} from 'test/utils/TestConstants.sol';
import {ContractDeploymentAddress} from 'test/utils/ContractDeploymentAddress.sol';
Expand All @@ -25,6 +29,7 @@ contract CommonE2EBase is DSTestPlus, TestConstants {
StorageMirror public storageMirror;
UpdateStorageMirrorGuard public updateStorageMirrorGuard;
GuardCallbackModule public guardCallbackModule;
BlockHeaderOracle public oracle;
ISafe public safe;
IGnosisSafeProxyFactory public gnosisSafeProxyFactory = IGnosisSafeProxyFactory(GNOSIS_SAFE_PROXY_FACTORY);

Expand Down Expand Up @@ -52,6 +57,10 @@ contract CommonE2EBase is DSTestPlus, TestConstants {
updateStorageMirrorGuard = new UpdateStorageMirrorGuard(guardCallbackModule); // deployer nonce 2
label(address(updateStorageMirrorGuard), 'UpdateStorageMirrorGuard');

vm.prank(deployer);
oracle = new BlockHeaderOracle(); // deployer nonce 3
label(address(oracle), 'MockOracle');

// Make sure the theoritical address was calculated correctly
assert(address(updateStorageMirrorGuard) == _updateStorageMirrorGuardTheoriticalAddress);

Expand Down
39 changes: 39 additions & 0 deletions solidity/test/unit/MockOracle.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.4 <0.9.0;

import {Test} from 'forge-std/Test.sol';
import {BlockHeaderOracle} from 'contracts/BlockHeaderOracle.sol';

abstract contract Base is Test {
event BlockHeaderUpdated(bytes _blockHeader, uint256 _blockTimestamp, uint256 _blockNumber);

BlockHeaderOracle public oracle;

function setUp() public {
oracle = new BlockHeaderOracle();
}
}

contract UnitBlockHeaderOracle is Base {
function testUpdateBlockHeader(bytes memory _blockHeader, uint256 _blockTimestamp, uint256 _blockNumber) public {
vm.expectEmit(true, true, true, true);
emit BlockHeaderUpdated(_blockHeader, _blockTimestamp, _blockNumber);
oracle.updateBlockHeader(_blockHeader, _blockTimestamp, _blockNumber);

assertEq(_blockHeader, oracle.blockHeader(), 'Block header should be saved');
assertEq(_blockTimestamp, oracle.blockTimestamp(), 'Block timestamp should be saved');
}

function testGetLatestBlockHeader() public {
bytes memory _blockHeader = '0x1234';
uint256 _blockTimestamp = 1234;
uint256 _blockNumber = 1234;

oracle.updateBlockHeader(_blockHeader, _blockTimestamp, _blockNumber);

(bytes memory _savedBlockHeader, uint256 _savedBlockTimestamp) = oracle.getLatestBlockHeader();

assertEq(_blockHeader, _savedBlockHeader, 'Block header should be saved');
assertEq(_blockTimestamp, _savedBlockTimestamp, 'Block timestamp should be saved');
}
}

0 comments on commit cd3eb25

Please sign in to comment.