Skip to content

Commit

Permalink
feat(pg): add support for arbitrary entry point functions in the user…
Browse files Browse the repository at this point in the history
…'s script
  • Loading branch information
sam-goldman committed Mar 3, 2024
1 parent 5b6ae62 commit 4dfc0ba
Show file tree
Hide file tree
Showing 23 changed files with 467 additions and 105 deletions.
7 changes: 7 additions & 0 deletions .changeset/rare-humans-remember.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@sphinx-labs/contracts': minor
'@sphinx-labs/plugins': minor
'@sphinx-labs/core': minor
---

Add support for arbitrary entry point functions in the user's script
11 changes: 9 additions & 2 deletions docs/cli-deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
The `deploy` command executes a deployment on a single network. This command executes the deployment from your local machine without using the DevOps Platform.

The following steps occur during this command:
1. **Simulation**: Sphinx simulates the deployment by invoking the script's `run()` function on a fork of the network. If a transaction reverts during the simulation, Sphinx will throw an error.
1. **Simulation**: Sphinx simulates the deployment by invoking the Forge script on a fork of the network. If a transaction reverts during the simulation, Sphinx will throw an error.
2. **Preview**: Sphinx displays the broadcasted transactions in a preview, which you'll be prompted to confirm.
3. **Execute**: Sphinx executes the deployment on the target network.
4. **Deployment Artifacts**: Sphinx will write deployment artifacts to your file system. See the [Deployment Artifacts](https://github.com/sphinx-labs/sphinx/blob/main/docs/deployment-artifacts.md) guide for more information.
Expand Down Expand Up @@ -68,8 +68,10 @@ pnpm sphinx deploy <SCRIPT_PATH> --network <NETWORK_NAME> [options]

### Options
- `--network <NETWORK_NAME>`: **Required**. The name of the network to deploy on, which must match a network in the `rpc_endpoints` section of your `foundry.toml`.
- `--sig <SIGNATURE [PARAMETERS...] | CALLDATA>` (Alias: `-s`): **Optional**. The signature of the function to call in the script, or raw calldata. Matches the interface of Forge Script's `--sig` parameter.
- **Default**: `run()`
- `--confirm`: **Optional**. Confirm the deployment without previewing it. Useful for automated deployments.
- `--target-contract <TARGET_CONTRACT>` (Alias: `--tc <TARGET_CONTRACT>`): **Optional**. Specify a contract within the script file. Necessary for scripts with multiple contracts.
- `--target-contract <TARGET_CONTRACT>` (Alias: `--tc`): **Optional**. Specify a contract within the script file. Necessary for scripts with multiple contracts.
- `--verify`: **Optional**. Verify the deployment on Etherscan.
- `--silent`: **Optional**. Silence the output except for error messages. Must be combined with `--confirm` to confirm the deployment without previewing it.

Expand All @@ -83,3 +85,8 @@ pnpm sphinx deploy <SCRIPT_PATH> --network <NETWORK_NAME> [options]
```bash
npx sphinx deploy ./path/to/script.s.sol --network anvil --confirm
```

3. Deploy a script located at `./path/to/script.s.sol` on Ethereum by calling the script's `deploy(uint256)` function with the argument `1234`:
```bash
npx sphinx deploy ./path/to/script.s.sol --network ethereum --sig 'deploy(uint256)' 1234
```
15 changes: 9 additions & 6 deletions docs/cli-existing-project.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ In this guide, you'll propose the deployment on the command line and then approv
6. [Update your deployment script](#6-update-your-deployment-script)\
a. [Import Sphinx](#a-import-sphinx)\
b. [Inherit from `Sphinx`](#b-inherit-from-sphinx)\
c. [Update your `run()` function](#c-update-your-run-function)\
c. [Add the `sphinx` modifier](#c-add-the-sphinx-modifier)\
d. [Remove broadcasts](#d-remove-broadcasts)\
e. [Handle new sender address](#e-handle-new-sender-address)\
f. [Add configuration options](#e-add-configuration-options)
Expand Down Expand Up @@ -120,11 +120,11 @@ contract MyDeploymentScript is
// ...
```

### c. Update your `run()` function
### c. Add the `sphinx` modifier

The entry point of your deployment script must be a `run()` function; it cannot be named anything else. Please change its name if necessary.
Navigate to the entry point function in your deployment script. This is typically a `run()` function.

Then, add a `sphinx` modifier to your `run` function:
Then, add a `sphinx` modifier to this function. For example:

```sol
function run() sphinx public {
Expand Down Expand Up @@ -196,7 +196,10 @@ If you can't get your test suite to pass, we're more than happy to help! Reach o

## 10. Propose on testnets

Copy and paste one of the following commands to propose your deployment with the DevOps Platform. Make sure to replace `<PATH_TO_FORGE_SCRIPT>` with the path to your Forge script. Also, make sure to replace `<NETWORK_NAMES>` with the testnets you want to deploy on, which must match the network names in the `rpc_endpoints` section of your `foundry.toml`.
Use one of the command templates below to propose your deployment. Make sure to update the following parts of the command:
* Replace `<PATH_TO_FORGE_SCRIPT>` with the path to your Forge script.
* Replace `<NETWORK_NAMES>` with the testnets you want to deploy on, which must match the network names in the `rpc_endpoints` section of your `foundry.toml`.
* If your script's entry point is a function other than `run()`, add `--sig [PARAMETERS]` to the command, where `[PARAMETERS]` is either the signature of the function to call in the script, or raw calldata. Sphinx's `--sig` parameter accepts the same arguments as Foundry's `--sig` parameter; see docs [here](https://github.com/sphinx-labs/sphinx/blob/main/docs/cli-propose.md#options).

Using Yarn or npm:

Expand All @@ -211,7 +214,7 @@ pnpm sphinx propose <PATH_TO_FORGE_SCRIPT> --networks <NETWORK_NAMES>
```

Here are the steps that occur when you run this command:
1. **Simulation**: Sphinx simulates the deployment by invoking the script's `run()` function on a fork of each network. If a transaction reverts during the simulation, Sphinx will throw an error.
1. **Simulation**: Sphinx simulates the deployment by invoking the Forge script on a fork of each network. If a transaction reverts during the simulation, Sphinx will throw an error.
2. **Preview**: Sphinx displays the broadcasted transactions in a preview, which you'll be prompted to confirm.
3. **Relay**: Sphinx submits the deployment to the website, where you'll approve it in the next step.

Expand Down
15 changes: 11 additions & 4 deletions docs/cli-propose.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
The `propose` command proposes a deployment to the Sphinx DevOps Platform.

The following steps occur when this command is run:
1. **Simulation**: Sphinx simulates the deployment by invoking the script's `run()` function on a fork of each network. If a transaction reverts during the simulation, Sphinx will throw an error.
1. **Simulation**: Sphinx simulates the deployment by invoking the Forge script on a fork of each network. If a transaction reverts during the simulation, Sphinx will throw an error.
2. **Preview**: Sphinx displays the broadcasted transactions in a preview, which the user will be prompted to confirm.
3. **Relay**: Sphinx submits the deployment to the Sphinx UI, where the user will approve it.

Expand Down Expand Up @@ -40,10 +40,12 @@ pnpm sphinx propose <SCRIPT_PATH> --networks <NETWORK_NAMES...|testnets|mainnets
- Arbitrary network names (e.g., `ethereum optimism arbitrum`): Propose on one or more networks, which must match the network names in the `rpc_endpoints` section of your `foundry.toml`.
- `testnets`: Propose on the test networks in your `sphinxConfig.testnets` array. Provides a convenient way to propose on many networks without specifying them on the command line. Requires additional configuration; see the [Configuration Options section for `sphinxConfig.testnets`](https://github.com/sphinx-labs/sphinx/blob/main/docs/configuration-options.md#string-testnets-optional).
- `mainnets`: Propose on the production networks in your `sphinxConfig.mainnets` array. Provides a convenient way to propose on many networks without specifying them on the command line. Requires additional configuration; see the [Configuration Options section for `sphinxConfig.mainnets`](https://github.com/sphinx-labs/sphinx/blob/main/docs/configuration-options.md#string-mainnets-optional).
- `--sig <SIGNATURE [PARAMETERS...] | CALLDATA>` (Alias: `-s`): **Optional**. The signature of the function to call in the script, or raw calldata. Matches the interface of Forge Script's `--sig` parameter.
- **Default**: `run()`
- `--confirm`: **Optional**. Confirm the proposal without previewing it. Useful for automating proposals.
- `--dry-run`: **Optional**. Perform a trial run without sending data to Sphinx's backend. Useful for testing and validation.
- `--silent`: **Optional**. Suppress output to display only error messages. Must be combined with `--confirm` to confirm the proposal without previewing it.
- `--target-contract <TARGET_CONTRACT>` (Alias: `--tc <TARGET_CONTRACT>`): **Optional**. Specify a contract in multi-contract scripts.
- `--target-contract <TARGET_CONTRACT>` (Alias: `--tc`): **Optional**. Specify a contract in multi-contract scripts.

## Examples
1. Propose a script located at `./path/to/script.s.sol` on Sepolia:
Expand All @@ -56,12 +58,17 @@ pnpm sphinx propose <SCRIPT_PATH> --networks <NETWORK_NAMES...|testnets|mainnets
npx sphinx propose ./path/to/script.s.sol --networks ethereum optimism arbitrum
```

3. Propose a script located at `./path/to/script.s.sol` on all networks in `sphinxConfig.testnets`, skipping the deployment preview:
3. Propose a script located at `./path/to/script.s.sol` on Ethereum by calling the script's `deploy(uint256)` function with the argument `1234`:
```bash
npx sphinx propose ./path/to/script.s.sol --networks ethereum --sig 'deploy(uint256)' 1234
```

4. Propose a script located at `./path/to/script.s.sol` on all networks in `sphinxConfig.testnets`, skipping the deployment preview:
```bash
npx sphinx propose ./path/to/script.s.sol --networks testnets --confirm
```

4. Dry run a proposal on all networks in `sphinxConfig.mainnets` using a script located at `./path/to/script.s.sol`:
5. Dry run a proposal on all networks in `sphinxConfig.mainnets` using a script located at `./path/to/script.s.sol`:
```bash
npx sphinx propose ./path/to/script.s.sol --networks mainnets --dry-run
```
2 changes: 1 addition & 1 deletion docs/cli-quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ pnpm sphinx propose script/HelloSphinx.s.sol --networks sepolia optimism_sepolia
```

Here are the steps that occur when you run this command:
1. **Simulation**: Sphinx simulates the deployment by invoking the script's `run()` function on a fork of each network. If a transaction reverts during the simulation, Sphinx will throw an error.
1. **Simulation**: Sphinx simulates the deployment by invoking the Forge script on a fork of each network. If a transaction reverts during the simulation, Sphinx will throw an error.
2. **Preview**: Sphinx displays the broadcasted transactions in a preview, which you'll be prompted to confirm.
3. **Relay**: Sphinx submits the deployment to the website, where you'll approve it in the next step.

Expand Down
2 changes: 1 addition & 1 deletion docs/writing-scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ address safe = safeAddress();

## The `sphinx` modifier

The entry point for your deployment must always be a `run()` function that has a `sphinx` modifier:
The entry point function in your script must always have a `sphinx` modifier. For example:

```sol
function run() public sphinx {
Expand Down
27 changes: 18 additions & 9 deletions packages/contracts/contracts/foundry/Sphinx.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { SphinxForkCheck } from "./SphinxForkCheck.sol";
/**
* @notice An abstract contract that the user must inherit in order to deploy with Sphinx.
* The main user-facing element of this contract is the `sphinx` modifier, which
* the user must include in their `run()` function. The rest of the logic is used
* the user must include in their entry point function. The rest of the logic is used
* internally by Sphinx to handle the process of collecting the user's contract
* deployments and function calls.
*
Expand Down Expand Up @@ -138,18 +138,23 @@ abstract contract Sphinx {
return (libraryVersion, forkInstalled);
}

function sphinxCollectProposal(string memory _deploymentInfoPath) external {
function sphinxCollectProposal(
bytes memory _scriptFunctionCalldata,
string memory _deploymentInfoPath
) external {
sphinxUtils.validateProposal(address(this));

string memory serializedDeploymentInfo = sphinxCollect(
ExecutionMode.Platform,
constants.managedServiceAddress()
constants.managedServiceAddress(),
_scriptFunctionCalldata
);

vm.writeFile(_deploymentInfoPath, serializedDeploymentInfo);
}

function sphinxCollectDeployment(
bytes memory _scriptFunctionCalldata,
ExecutionMode _executionMode,
string memory _deploymentInfoPath,
string memory _systemContractsFilePath
Expand Down Expand Up @@ -178,13 +183,18 @@ abstract contract Sphinx {
// Gnosis Safe ensures that its nonce is treated like a contract instead of an EOA.
sphinxUtils.deploySphinxSystem(systemContracts);

string memory serializedDeploymentInfo = sphinxCollect(_executionMode, deployer);
string memory serializedDeploymentInfo = sphinxCollect(
_executionMode,
deployer,
_scriptFunctionCalldata
);
vm.writeFile(_deploymentInfoPath, serializedDeploymentInfo);
}

function sphinxCollect(
ExecutionMode _executionMode,
address _executor
address _executor,
bytes memory _scriptFunctionCalldata
) private returns (string memory) {
address safe = safeAddress();
address module = sphinxModule();
Expand Down Expand Up @@ -237,9 +247,8 @@ abstract contract Sphinx {
uint256 snapshotId = vm.snapshot();

vm.startStateDiffRecording();
// Delegatecall the `run()` function on this contract to collect the transactions. This
// pattern gives us flexibility to support function names other than `run()` in the future.
(bool success, ) = address(this).delegatecall(abi.encodeWithSignature("run()"));
// Delegatecall the entry point function on this contract to collect the transactions.
(bool success, ) = address(this).delegatecall(_scriptFunctionCalldata);
// Throw an error if the deployment script fails. The error message in the user's script is
// displayed by Foundry's stack trace, so it'd be redundant to include the data returned by
// the delegatecall in our error message.
Expand Down Expand Up @@ -294,7 +303,7 @@ abstract contract Sphinx {
}

/**
* @notice A modifier that the user must include on their `run()` function when using Sphinx.
* @notice A modifier that the user must include on their entry point function when using Sphinx.
* This modifier mainly performs validation on the user's configuration and environment.
*/
modifier sphinx() {
Expand Down
4 changes: 2 additions & 2 deletions packages/contracts/contracts/foundry/SphinxUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -871,8 +871,8 @@ contract SphinxUtils is SphinxConstants, StdUtils {
* CREATE2 opcode instead of the default CREATE2 factory, then Create2 would probably be
* added as a kind here.
* - The call depth is equal to 2. The expected depth is 2 because the depth value starts
* at 1 and because we initiate the collection process by doing a delegatecall to the run()
* function so the depth is 2 by the time any transactions get sent in the users script.
* at 1 and because we initiate the collection process by doing a delegatecall to the entry
* point function so the depth is 2 by the time any transactions get sent in the users script.
*/
function isRootAccountAccess(
Vm.AccountAccess memory _access,
Expand Down
Loading

0 comments on commit 4dfc0ba

Please sign in to comment.