Skip to content

Commit

Permalink
fix(pg): validate that the chain ID is correct in the user's script
Browse files Browse the repository at this point in the history
  • Loading branch information
sam-goldman committed Nov 7, 2023
1 parent 1b413b2 commit c2202c3
Show file tree
Hide file tree
Showing 13 changed files with 125 additions and 88 deletions.
5 changes: 5 additions & 0 deletions .changeset/four-terms-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sphinx-labs/plugins': patch
---

Validate that the chain ID is correct in the user's script
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ Sphinx automates the smart contract deployment process by funding, executing, an
## Key features:

* **Compatible with Forge Scripts**: You can integrate Sphinx with minimal changes to your existing Forge scripts.

* **Gasless deployments**: You don't need to worry about securing a funded private key or getting native gas tokens on any chain. Instead, simply maintain a balance of USDC on a single chain to fund deployments across all chains.

* **One-Click Multichain Deployments**: Approve deployments across up to 11 supported networks with a single click. Sphinx will execute the deployment on each chain in parallel.
* **One-Click Multichain Deployments**: Your project owners can approve deployments across up to 11 supported networks by signing a single meta transaction. Sphinx will execute the deployment on each chain in parallel.

* **Deployments in CI**: Initiating deployments from a CI process has obvious benefits such as reproducibility and consistency, but it hasn't been practical until now. With Sphinx, you can propose deployments from your CI process, then approve it in our UI (all gaslessly, of course). Sphinx's backend will execute the deployment on your behalf. If you'd rather not use a CI process, you can propose deployments from your local machine instead.

* **Automatic Etherscan verification**

* **Compatible with Forge Scripts**: You can integrate Sphinx with minimal changes to your existing Forge scripts.

## Request access

Sphinx is currently invite-only. [Request access on our website.](https://sphinx.dev)
Expand Down
4 changes: 1 addition & 3 deletions docs/cli-existing-project.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ extra_output = ['storageLayout']
fs_permissions = [{ access = "read-write", path = "./"}]
```

We also highly recommend setting `optimizer = 'false'` for development because this makes compilation happen ~5x faster. See the [Foundry docs](https://book.getfoundry.sh/reference/forge/forge-build?highlight=optimizer#conditional-optimizer-usage) for more details.

## 6. Add remappings

You probably already have remappings either in your `foundry.toml` or `remappings.txt` file. If you don't, we recommend adding a `remappings.txt` file in the root of your repository.
Expand Down Expand Up @@ -141,7 +139,7 @@ npx sphinx deploy <script/path>/HelloSphinx.s.sol --network anvil

You'll be shown a preview of your deployment and prompted to confirm. Any transactions that are broadcasted by Foundry will be included in the deployment.

Sphinx will automatically generate deployment artifacts, which are in the same format as [`hardhat-deploy`](https://github.com/wighawag/hardhat-deploy). When the deployment completes, you'll find the deployment artifacts written to `./deployments/anvil-31337.json`.
Sphinx will automatically generate deployment artifacts, which are in the same format as [hardhat-deploy](https://github.com/wighawag/hardhat-deploy). When the deployment completes, you'll find the deployment artifacts written to `./deployments/anvil-31337.json`.

If you'd like to use this command to deploy on a live network, you can verify your contracts on block explorers using the `--verify` flag.

Expand Down
2 changes: 1 addition & 1 deletion docs/cli-quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ npx sphinx deploy ./script/HelloSphinx.s.sol --network anvil

You'll be shown a preview of your deployment and prompted to confirm. Any transactions that are broadcasted by Foundry will be included in the deployment.

Sphinx will automatically generate deployment artifacts, which are in the same format as [`hardhat-deploy`](https://github.com/wighawag/hardhat-deploy). When the deployment completes, you'll find the deployment artifacts written to `./deployments/anvil-31337.json`.
Sphinx will automatically generate deployment artifacts, which are in the same format as [hardhat-deploy](https://github.com/wighawag/hardhat-deploy). When the deployment completes, you'll find the deployment artifacts written to `./deployments/anvil-31337.json`.

If you'd like to use this command to deploy on a live network, you can verify your contracts on block explorers using the `--verify` flag.

Expand Down
80 changes: 40 additions & 40 deletions docs/ops-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ This guide will introduce you to the Sphinx DevOps platform by walking you throu
1. [Prerequisites](#1-prerequisites)
2. [Get testnet ETH](#2-get-testnet-eth)
3. [Get credentials](#3-get-credentials)
4. [Add your credentials](#4-add-your-credentials)
5. [Configure your script](#5-configure-your-script)
6. [Add RPC endpoints](#6-add-rpc-endpoints)
7. [Propose on testnets](#7-propose-on-testnets)
8. [Propose on mainnet (optional)](#8-propose-on-mainnet-optional)
4. [Get an RPC endpoint API key](#4-get-an-rpc-endpoint-api-key)
5. [Add environment variables](#5-add-environment-variables)
6. [Configure your script](#6-configure-your-script)
7. [Add RPC endpoints](#7-add-rpc-endpoints)
8. [Propose on testnets](#8-propose-on-testnets)
9. [Propose on mainnet (optional)](#9-propose-on-mainnet-optional)

## 1. Prerequisites

Expand All @@ -32,11 +33,16 @@ You'll need a small amount of testnet ETH on Optimism Goerli, which you can get

You'll need a Sphinx API key and an organization ID. You can get these on the [Sphinx UI](https://www.sphinx.dev/).

## 4. Add your credentials
## 4. Get an RPC endpoint API key

In your `.env` file, add your Sphinx API key from the previous step:
We recommend getting a private API key from an RPC node provider like [Alchemy](https://www.alchemy.com/). Public RPC endpoints can be flaky, so we don't recommend them for this guide.

## 5. Add environment variables

In your `.env` file, add your Sphinx API key and your RPC endpoint API key from the previous steps:
```
SPHINX_API_KEY=<your API key>
RPC_API_KEY=<your API key>
```

Also, in your `.env` file, add the private key of your EOA:
Expand All @@ -47,53 +53,46 @@ PROPOSER_PRIVATE_KEY=<private key>

We'll use this EOA to propose the deployment on the command line in a later step.

## 5. Configure your script
> For the purpose of this guide, we'll use the same EOA to propose the deployment on the command line and approve it in the Sphinx UI. However, in a production environment, we recommend creating a new private key to use specifically for proposals, since the private key will either be stored locally in a `.env` file or as a secret in your CI process.
Navigate to your deployment script.
## 6. Configure your script

First, add the import:
```
import { Network } from "@sphinx-labs/plugins/SphinxPluginTypes.sol";
```

In your `setUp` function, update the `sphinxConfig.owners` array to include the address of your EOA:
First, navigate to your deployment script.

Add the import:
```
sphinxConfig.owners = [<your EOA address>];
import { Network } from "@sphinx-labs/plugins/SphinxPluginTypes.sol";
```

Then, copy and paste the following config options into your `setUp` function:
Then, copy and paste the following config template into your `setUp` function:
```
sphinxConfig.orgId = <org ID>;
sphinxConfig.proposers = [<your proposer address>];
sphinxConfig.mainnets = [];
sphinxConfig.testnets = [
Network.goerli,
Network.optimism_goerli,
Network.arbitrum_goerli,
Network.polygon_mumbai,
Network.bnb_testnet,
Network.gnosis_chiado
];
sphinxConfig.owners = [<your EOA address>];
sphinxConfig.proposers = [<your EOA address>];
sphinxConfig.orgId = <org ID>;
sphinxConfig.projectName = "My First Project";
sphinxConfig.threshold = 1;
sphinxConfig.mainnets;
sphinxConfig.testnets = [
Network.goerli,
Network.optimism_goerli,
Network.arbitrum_goerli
];
```

Set the `sphinxConfig.orgId` field to the organization ID that you got from the Sphinx UI, and set the `sphinxConfig.proposers` array to include the address of your EOA.
Fill in the template with your values. The `orgId` is a public field, so you don't need to keep it secret.

## 6. Add RPC endpoints
## 7. Add RPC endpoints

You'll need to update your `foundry.toml` to include an RPC endpoint for each network. You can use these public RPC endpoints:
Include an RPC endpoint for each network in your `foundry.toml`. For example, if you're using Alchemy, your `foundry.toml` might look like this:

```toml
```
[rpc_endpoints]
goerli = "https://rpc.ankr.com/eth_goerli"
optimism_goerli = "https://rpc.ankr.com/optimism_testnet/"
arbitrum_goerli = "https://goerli-rollup.arbitrum.io/rpc"
bnb_testnet = "https://bsc-testnet.publicnode.com"
gnosis_chiado = "https://rpc.chiadochain.net"
polygon_mumbai = "https://rpc.ankr.com/polygon_mumbai"
goerli = "https://eth-goerli.g.alchemy.com/v2/${RPC_API_KEY}"
optimism_goerli = "https://opt-goerli.g.alchemy.com/v2/${RPC_API_KEY}"
arbitrum_goerli = "https://arb-goerli.g.alchemy.com/v2/${RPC_API_KEY}"
```

## 7. Propose on testnets
## 8. Propose on testnets

Copy and paste the following command, replacing `<path/to/your-script.s.sol>` with the actual path to your deployment script:

Expand All @@ -105,9 +104,10 @@ Sphinx will propose all transactions that are broadcasted by Foundry.

Follow the instructions in the terminal to finish the rest of the deployment.

## 8. Propose on mainnet (optional)
## 9. Propose on mainnet (optional)

To propose a deployment on mainnet, you simply need to add a `sphinxConfig.mainnets` option to the `setUp` function in your deployment script. For example:

```
sphinxConfig.mainnets = [
Network.optimism
Expand Down
2 changes: 1 addition & 1 deletion docs/ops-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Deployments are a three-step process with the DevOps platform.

1. **Propose**: The deployment is proposed on the command line or in a CI process. This creates a meta transaction that's signed by the proposer's private key then relayed to Sphinx's back-end. We recommend creating a new private key to use specifically for proposals, since the private key will either be stored in a `.env` file or as a secret in your CI process.
1. **Propose**: The deployment is proposed on the command line or in a CI process. This creates a meta transaction that's signed by the proposer's private key then relayed to Sphinx's back-end. We recommend creating a new private key to use specifically for proposals, since the private key will either be stored locally in a `.env` file or as a secret in your CI process.
2. **Fund**: With Sphinx, you don't need to handle native gas tokens for your deployments. Instead, you'll cover the cost of your deployments by depositing USDC into a `SphinxBalance` contract. On production networks, you'll fund your deployments on Optimism Mainnet. Likewise, on testnets, you'll fund your deployments on Optimism Goerli. We'll provide you with free USDC on Optimism Goerli to fund your deployments on testnets.
3. **Approve**: The project owner(s) sign a meta transaction to approve the deployment in the Sphinx UI.

Expand Down
37 changes: 33 additions & 4 deletions docs/troubleshooting-guide.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Troubleshooting Guide

This guide covers some common issues you might encounter using Sphinx. If your question isn't answered here, please reach out in the [Discord](https://discord.gg/7Gc3DK33Np).
This guide covers some common issues you might encounter using Sphinx. If your question isn't answered here, please reach out in our [Discord](https://discord.gg/7Gc3DK33Np).

## Table of Contents

- [Labeling contracts](#labeling-contracts)
- [Slow compilation speed](#slow-compilation-speed)
- [`Ineffective mark-compacts near heap limit allocation failed` error](#ineffective-mark-compacts-near-heap-limit-allocation-failed-error)

### Labeling contracts
## Labeling contracts

When Sphinx can't infer the contract that corresponds to an address, you'll be asked to label it yourself. This allows Sphinx to write the contract's deployment artifact and verify it on Etherscan.

Expand All @@ -20,7 +21,35 @@ sphinxLabel(address(token), "src/tokens/MyToken.sol:MyToken");

You must use the **fully qualified name** of the contract, which is in the format `full/path/to/SourceFile.sol:ContractName`, as shown in the example above.

If you're having trouble finding the contract that corresponds to an address, we recommend using `console.log`. You can import it into your script using `import "forge-std/console.sol";`.
If you're having trouble finding the contract that corresponds to an address, we recommend using `console.log`. You can import it into your script using:

### `Ineffective mark-compacts near heap limit allocation failed` error
```
import "forge-std/console.sol";
```

Then, you can log the addresses of your contracts:

```
MyToken token = new MyToken{ salt: bytes32(0) }();
console.log('MyToken', address(token));
```

## Slow compilation speed

Sphinx may slow down the compilation speed of your script because the `Sphinx.sol` contract is rather large. You can speed up compilation during development by disabling the Solidity compiler optimizer. One approach is to create a new profile in your `foundry.toml` file. For example:

```
[profile.lite]
optimizer = false
```

Then, you can prefix any `forge` command with `FOUNDRY_PROFILE=lite` to reduce its compilation time. For example:

```
FOUNDRY_PROFILE=lite forge build
```

If you don't include this prefix in a `forge` command, Foundry will continue to use the default profile in your `foundry.toml`, which is called `profile.default`.

## `Ineffective mark-compacts near heap limit allocation failed` error
This bug can occur in repositories that have a very large number of contracts in them. This causes your build info artifact files to be extremely large, which can cause memory issues when using Sphinx. You can resolve this issue by running `forge clean`, which clears the artifacts directory, including the build info files.
4 changes: 0 additions & 4 deletions packages/demo/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ build_info = true
extra_output = ['storageLayout']
fs_permissions = [{ access = "read-write", path = "./"}]
allow_paths = ["../.."]
# We highly recommend setting the optimizer to 'false' for development because
# this makes compilation happen ~5x faster. See here for more details:
# https://book.getfoundry.sh/reference/forge/forge-build?highlight=optimizer#conditional-optimizer-usage
optimizer = false
remappings=[
'forge-std/=node_modules/forge-std/src',
'ds-test/=node_modules/ds-test/src/',
Expand Down
2 changes: 1 addition & 1 deletion packages/demo/test/init.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { getFoundryConfigOptions } from '@sphinx-labs/plugins/src/foundry/option
const deploymentArtifactDir = 'deployments'

const provider = new SphinxJsonRpcProvider(`http://127.0.0.1:8545`)
const contractAddress = '0xbd0DB8399aBE4EbC7247561C43a395E190633e07'
const contractAddress = '0x67AA37B2fb458501C3bB1Db312017a12AC3fD8cD'

describe('Init CLI command', () => {
let contractPath: string
Expand Down
28 changes: 25 additions & 3 deletions packages/plugins/contracts/foundry/Sphinx.sol
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ abstract contract Sphinx {
string memory rpcUrl = vm.rpcUrl(_networkName);
sphinxUtils.validateProposal(_proposer, _networkName, sphinxConfig);

DeploymentInfo memory deploymentInfo = sphinxCollect(sphinxUtils.isLiveNetworkFFI(rpcUrl));
DeploymentInfo memory deploymentInfo = sphinxCollect(
_networkName,
sphinxUtils.isLiveNetworkFFI(rpcUrl)
);

vm.writeFile(_deploymentInfoPath, vm.toString(abi.encode(deploymentInfo)));
}
Expand Down Expand Up @@ -138,11 +141,30 @@ abstract contract Sphinx {
sphinxConfig.proposers.push(deployer);
}

DeploymentInfo memory deploymentInfo = sphinxCollect(isLiveNetwork);
DeploymentInfo memory deploymentInfo = sphinxCollect(_networkName, isLiveNetwork);
vm.writeFile(_deploymentInfoPath, vm.toString(abi.encode(deploymentInfo)));
}

function sphinxCollect(bool _isLiveNetwork) private returns (DeploymentInfo memory) {
function sphinxCollect(
string memory _networkName,
bool _isLiveNetwork
) private returns (DeploymentInfo memory) {
NetworkInfo memory networkInfo = sphinxUtils.findNetworkInfoByName(_networkName);
require(
block.chainid == networkInfo.chainId,
string(
abi.encodePacked(
"Sphinx: Detected an unexpected chain ID for the network: ",
_networkName,
".\nExpected: ",
vm.toString(networkInfo.chainId),
"\nActual: ",
vm.toString(block.chainid),
"\nAre you sure you're using the correct RPC endpoint?"
)
)
);

ISphinxAuth auth = ISphinxAuth(sphinxUtils.getSphinxAuthAddress(sphinxConfig));
ISphinxManager manager = ISphinxManager(sphinxManager());

Expand Down
22 changes: 8 additions & 14 deletions packages/plugins/src/cli/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,23 +95,17 @@ export const deploy = async (
}

const provider = new SphinxJsonRpcProvider(forkUrl)
// Force re-compile the contracts if this step hasn't been disabled and if we're on a live
// network. This ensures that we're using the correct artifacts for the deployment. This is mostly
// out of an abundance of caution, since using the incorrect contract artifact will prevent us
// from writing the deployment artifact.
if (!skipForceRecompile && (await isLiveNetwork(provider))) {
const forgeCleanArgs = silent ? ['clean', '--silent'] : ['clean']
const { status: cleanStatus } = spawnSync(`forge`, forgeCleanArgs, {
stdio: 'inherit',
})
// Exit the process if the clean fails.
if (cleanStatus !== 0) {
process.exit(1)
}
}

// Compile to make sure the user's contracts are up to date.
const forgeBuildArgs = silent ? ['build', '--silent'] : ['build']
// Force re-compile the contracts unless it's explicitly been disabled or if we're not on a live
// network. This ensures that we're using the correct artifacts for live network deployments. This
// is mostly out of an abundance of caution, since using an incorrect contract artifact will
// prevent us from writing the deployment artifact for that contract.
if (!skipForceRecompile && (await isLiveNetwork(provider))) {
forgeBuildArgs.push('--force')
}

const { status: compilationStatus } = spawnSync(`forge`, forgeBuildArgs, {
stdio: 'inherit',
})
Expand Down
17 changes: 7 additions & 10 deletions packages/plugins/src/cli/propose/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,19 +216,16 @@ export const propose = async (
}
const proposer = new ethers.Wallet(proposerPrivateKey)

// Compile to make sure the user's contracts are up to date.
const forgeBuildArgs = silent ? ['build', '--silent'] : ['build']
// Force re-compile the contracts unless it's explicitly been disabled. This ensures that we're
// using the correct artifacts for proposals. This is mostly out of an abundance of caution, since
// using an incorrect contract artifact will prevent us from creating the contract's deployment
// and verifying it on Etherscan.
if (!skipForceRecompile) {
const forgeCleanArgs = silent ? ['clean', '--silent'] : ['clean']
const { status: cleanStatus } = spawnSync(`forge`, forgeCleanArgs, {
stdio: 'inherit',
})
// Exit the process if the clean fails.
if (cleanStatus !== 0) {
process.exit(1)
}
forgeBuildArgs.push('--force')
}

// Compile to make sure the user's contracts are up to date.
const forgeBuildArgs = silent ? ['build', '--silent'] : ['build']
const { status: compilationStatus } = spawnSync(`forge`, forgeBuildArgs, {
stdio: 'inherit',
})
Expand Down
4 changes: 0 additions & 4 deletions packages/plugins/src/sample-project/sample-foundry-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ build_info = true
extra_output = ['storageLayout']
fs_permissions = [{ access = "read-write", path = "./"}]
allow_paths = ["../.."]
# We highly recommend setting the optimizer to 'false' for development because
# this makes compilation happen ~5x faster. See here for more details:
# https://book.getfoundry.sh/reference/forge/forge-build?highlight=optimizer#conditional-optimizer-usage
optimizer = false
remappings=[
'forge-std/=node_modules/forge-std/src',
'ds-test/=node_modules/ds-test/src/',
Expand Down

0 comments on commit c2202c3

Please sign in to comment.