Skip to content

Commit

Permalink
fix(pg): use artifact inference in Foundry plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
sam-goldman committed Jan 16, 2024
1 parent d04b9f9 commit b802adf
Show file tree
Hide file tree
Showing 16 changed files with 104 additions and 556 deletions.
7 changes: 7 additions & 0 deletions .changeset/many-wolves-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@sphinx-labs/contracts': patch
'@sphinx-labs/plugins': patch
'@sphinx-labs/core': patch
---

Use artifact inference in Foundry plugin
27 changes: 0 additions & 27 deletions docs/troubleshooting-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,11 @@ This guide covers some common issues you might encounter using Sphinx. If your q

## Table of Contents

- [Labeling contracts](#labeling-contracts)
- [Slow compilation speed](#slow-compilation-speed)
- [Errors](#errors)
- [`Ineffective mark-compacts near heap limit allocation failed`](#ineffective-mark-compacts-near-heap-limit-allocation-failed)
- [`EvmError: MemoryLimitOOG`](#evmerror-memorylimitoog)

## Labeling contracts

When Sphinx can't infer the contract corresponding to an address, an error will be thrown asking you to label it yourself. This makes it possible for Sphinx to verify the contract on Etherscan and create a deployment artifact for it.

You can label a contract in your deployment script with the `sphinxLabel` function, which is inherited from `Sphinx.sol`. For example:

```sol
MyToken token = new MyToken{ salt: bytes32(0) }();
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 corresponding to an address, we recommend using `console.log`. You can import it into your script using:

```
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 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:
Expand Down
6 changes: 0 additions & 6 deletions packages/contracts/contracts/foundry/SphinxPluginTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ struct DeploymentInfo {
SphinxConfig newConfig;
ExecutionMode executionMode;
InitialChainState initialState;
Label[] labels;
bool arbitraryChain;
}

Expand Down Expand Up @@ -124,11 +123,6 @@ struct SphinxConfig {
uint256 saltNonce;
}

struct Label {
address addr;
string fullyQualifiedName;
}

struct DeployOptions {
bytes32 salt;
string referenceName;
Expand Down
1 change: 0 additions & 1 deletion packages/contracts/contracts/foundry/SphinxUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
InitialChainState,
OptionalAddress,
Wallet,
Label,
ExecutionMode,
SystemContractInfo
} from "./SphinxPluginTypes.sol";
Expand Down
6 changes: 0 additions & 6 deletions packages/core/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ export type DeploymentInfo = {
newConfig: SphinxConfig
executionMode: ExecutionMode
initialState: InitialChainState
labels: Array<Label>
arbitraryChain: boolean
}

Expand All @@ -124,11 +123,6 @@ export type UserAddressOverrides = {
address: string
}

export type Label = {
addr: string
fullyQualifiedName: string
}

export type SphinxConfigWithAddresses = SphinxConfig & {
safeAddress: string
moduleAddress: string
Expand Down
17 changes: 7 additions & 10 deletions packages/core/src/preview.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { yellow, green, blue, bold } from 'chalk'
import { yellow, green, bold } from 'chalk'

import { DecodedAction, ParsedConfig } from './config/types'
import {
Expand Down Expand Up @@ -127,20 +127,17 @@ export const getPreviewString = (

// Warn about unlabeled addresses
if (preview.unlabeledAddresses.size > 0) {
const troubleshootingGuideLink = blue.underline(
`https://github.com/sphinx-labs/sphinx/blob/main/docs/troubleshooting-guide.md#labeling-contracts\n\n`
)
previewString += `${yellow.bold(
`Warning: Sphinx can't infer the contracts that correspond to the following addresses:\n`
`Warning: Sphinx couldn't find a contract artifact for the following addresses:\n`
)}`
previewString += `${Array.from(preview.unlabeledAddresses)
.map((e) => yellow(`- ${e}`))
.join('\n')}\n`
previewString +=
yellow(
`If you'd like Sphinx to verify any of these contracts on Etherscan or create their deployment artifacts,\n` +
`please label them in your script. See the troubleshooting guide for more information:\n`
) + troubleshootingGuideLink
previewString += yellow(
`This typically happens when deploying contracts using hardcoded bytecode and no \n` +
`associated source file. Sphinx will not create a deployment artifact or attempt \n` +
`Etherscan verification for any address in the list above.\n\n`
)
}

if (includeConfirmQuestion) {
Expand Down
12 changes: 0 additions & 12 deletions packages/core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import {
ActionInput,
RawCreate2ActionInput,
RawActionInput,
Label,
ParsedConfig,
Create2ActionInput,
FunctionCallActionInput,
Expand Down Expand Up @@ -928,17 +927,6 @@ export const isString = (str: string | null | undefined): str is string => {
return typeof str === 'string'
}

export const isLabel = (l: Label | undefined): l is Label => {
if (l === undefined) {
return false
}

return (
typeof (l as Label).addr === 'string' &&
typeof (l as Label).fullyQualifiedName === 'string'
)
}

export const toSphinxTransaction = (
actionInput: RawActionInput
): SphinxTransaction => {
Expand Down
41 changes: 0 additions & 41 deletions packages/plugins/contracts/foundry/Sphinx.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
DeploymentInfo,
NetworkInfo,
Wallet,
Label,
SphinxTransaction,
ExecutionMode,
SystemContractInfo
Expand Down Expand Up @@ -65,8 +64,6 @@ abstract contract Sphinx {
*/
SphinxConfig public sphinxConfig;

Label[] private labels;

SphinxConstants private constants;

SphinxUtils private sphinxUtils;
Expand Down Expand Up @@ -173,18 +170,6 @@ abstract contract Sphinx {
// the delegatecall in our error message.
require(success, "Sphinx: Deployment script failed.");

// Set the labels. We do this after running the user's script because the user may assign
// labels in their deployment. We use a for-loop instead of directly assigning the labels to
// prevent an error when compiling with `viaIR` and the solc optimizer enabled (runs =
// 200) using solc v0.8.5.
deploymentInfo.labels = new Label[](labels.length);
for (uint i = 0; i < labels.length; i++) {
deploymentInfo.labels[i] = Label({
addr: labels[i].addr,
fullyQualifiedName: labels[i].fullyQualifiedName
});
}

return deploymentInfo;
}

Expand Down Expand Up @@ -293,32 +278,6 @@ abstract contract Sphinx {
);
}

function sphinxLabel(address _addr, string memory _fullyQualifiedName) internal {
for (uint256 i = 0; i < labels.length; i++) {
Label memory label = labels[i];
if (label.addr == _addr) {
require(
keccak256(abi.encodePacked(_fullyQualifiedName)) ==
keccak256(abi.encodePacked(label.fullyQualifiedName)),
string(
abi.encodePacked(
"Sphinx: The address ",
vm.toString(_addr),
" was labeled with two names:\n",
label.fullyQualifiedName,
"\n",
_fullyQualifiedName,
"\nPlease choose one label."
)
)
);
return;
}
}

labels.push(Label(_addr, _fullyQualifiedName));
}

/**
* @notice Return the user's config ABI encoded. This is useful for retrieving the config
* off-chain. We ABI encode the config because it's difficult to decode complex
Expand Down
14 changes: 0 additions & 14 deletions packages/plugins/contracts/test/conflictingNameContracts/First.sol

This file was deleted.

This file was deleted.

62 changes: 6 additions & 56 deletions packages/plugins/contracts/test/script/Cases.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ import { Sphinx } from "../../foundry/Sphinx.sol";
import { CREATE3 } from "solady/utils/CREATE3.sol";
import { ConstructorDeploysContract } from "../../../contracts/test/ConstructorDeploysContract.sol";
import { Fallback } from "../../../contracts/test/Fallback.sol";
import {
ConflictingNameContract
} from "../../../contracts/test/conflictingNameContracts/First.sol";

contract Simple is Script, Sphinx {
constructor() {
Expand Down Expand Up @@ -39,63 +36,16 @@ contract Simple is Script, Sphinx {
Fallback fallbackContract = Fallback(CREATE3.deploy(bytes32(0), fallbackInitCode, 0));
fallbackContract.set(1);

// Deploys contract that deploys another contract in its constructor using create2
// The contract deployed is labeled
ConstructorDeploysContract constructorDeploysContract = new ConstructorDeploysContract{
salt: bytes32(uint(1))
}(1);
sphinxLabel(
address(constructorDeploysContract.myContract()),
"contracts/test/ConstructorDeploysContract.sol:DeployedInConstructor"
);
// Deploys contract that deploys another contract in its constructor using create2.
// The deployed contract is automatically labeled because it has a source file.
new ConstructorDeploysContract{ salt: bytes32(uint(1)) }(1);

// Deploys contract that deploys another contract in its constructor using create3
// Both the parent and child are labeled
// Deploys contract that deploys another contract in its constructor using create3.
// Both the parent and child are labeled because they both have source files.
bytes memory constructorDeploysContractInitCode = abi.encodePacked(
type(ConstructorDeploysContract).creationCode,
abi.encode(2)
);
ConstructorDeploysContract constructorDeploysContractCreate3 = ConstructorDeploysContract(
CREATE3.deploy(bytes32(uint(1)), constructorDeploysContractInitCode, 0)
);
sphinxLabel(
address(constructorDeploysContractCreate3),
"contracts/test/ConstructorDeploysContract.sol:ConstructorDeploysContract"
);
sphinxLabel(
address(constructorDeploysContractCreate3.myContract()),
"contracts/test/ConstructorDeploysContract.sol:DeployedInConstructor"
);

// Deploys contract that deploys another contract in its constructor using create2
// The contract is not labeled
new ConstructorDeploysContract{ salt: bytes32(uint(2)) }(3);

// Deploys contract that deploys another contract in its constructor using create3
// Neither contract is labeled
bytes memory constructorDeploysContractUnlabeledInitCode = abi.encodePacked(
type(ConstructorDeploysContract).creationCode,
abi.encode(4)
);
CREATE3.deploy(bytes32(uint(2)), constructorDeploysContractUnlabeledInitCode, 0);

// Deploy a contract whose name is not unique in the source directory
// The contract is labeled
ConflictingNameContract conflictingNameContract = new ConflictingNameContract{ salt: 0 }(1);
sphinxLabel(
address(conflictingNameContract),
"contracts/test/conflictingNameContracts/First.sol:ConflictingNameContract"
);

// Deploy a contract whose name is not unique in the source directory
// The contract is not labeled
new ConflictingNameContract{ salt: bytes32(uint(1)) }(2);

// Deploy a contract whose name is not unique in the source directory
// We interact with the contract, so it does not require a label
ConflictingNameContract conflictingNameContractInteract = new ConflictingNameContract{
salt: bytes32(uint(2))
}(3);
conflictingNameContractInteract.set(5);
CREATE3.deploy(bytes32(uint(1)), constructorDeploysContractInitCode, 0);
}
}
5 changes: 2 additions & 3 deletions packages/plugins/script/Sample.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.0;

import {Script, console} from "sphinx-forge-std/Script.sol";
import {Sphinx} from "../contracts/foundry/Sphinx.sol";
import {Network, Label} from "@sphinx-labs/contracts/contracts/foundry/SphinxPluginTypes.sol";
import {Network} from "@sphinx-labs/contracts/contracts/foundry/SphinxPluginTypes.sol";
import {MyContract1} from "../contracts/test/MyContracts.sol";
import {CREATE3} from "solady/utils/CREATE3.sol";

Expand Down Expand Up @@ -42,7 +42,6 @@ contract Sample is Sphinx {

bytes memory initCode =
abi.encodePacked(type(MyContract1).creationCode, abi.encode(1, 2, address(1), address(2)));
address deployed = CREATE3.deploy(bytes32(0), initCode, 0);
sphinxLabel(deployed, "contracts/test/MyContracts.sol:MyContract1");
CREATE3.deploy(bytes32(0), initCode, 0);
}
}
Loading

0 comments on commit b802adf

Please sign in to comment.