Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ cache
out
node_modules

.DS_Store
.DS_Store
broadcast/
197 changes: 195 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,24 @@ contract HuffDeployerExample {
// To call a function on the deployed contract, create an interface and wrap the address like so
Number number = Number(addr);
}

function deployForProduction() public {
// Deploy to production network with broadcast enabled
address addr = HuffDeployer
.config()
.set_broadcast(true) // Enable production deployment
.with_deployer(tx.origin) // Set deployer to transaction origin
.deploy("test/contracts/Number");

Number number = Number(addr);
}
}
```

To deploy a Huff contract with constructor arguments, you can _chain_ commands onto the HuffDeployer.

**Note**: All the examples below show both test mode (using `vm.prank()` for simulation) and production mode (using `vm.broadcast()` for real deployment). The production mode enables actual on-chain deployment to live networks.

For example, to deploy the contract [`src/test/contracts/Constructor.huff`](src/test/contracts/Constructor.huff) with arguments `(uint256(0x420), uint256(0x420))`, you are encouraged to follow the logic defined in the `deploy` function of the `HuffDeployerArguments` contract below.

```solidity
Expand All @@ -71,7 +84,7 @@ interface Constructor {

contract HuffDeployerArguments {
function deploy() public {
// Deploy the contract with arguments
// Deploy the contract with arguments (test mode)
address addr = HuffDeployer
.config()
.with_args(bytes.concat(abi.encode(uint256(0x420)), abi.encode(uint256(0x420))))
Expand All @@ -85,6 +98,20 @@ contract HuffDeployerArguments {
assert(construct.getArgTwo() == uint256(0x420));
}

function deployProduction() public {
// Deploy the contract with arguments (production mode)
address addr = HuffDeployer
.config()
.set_broadcast(true) // Enable production deployment
.with_deployer(msg.sender) // Set deployer address
.with_args(bytes.concat(abi.encode(uint256(0x420)), abi.encode(uint256(0x420))))
.deploy("test/contracts/Constructor");

Constructor construct = Constructor(addr);

// Note: In production, you would verify deployment via external calls or events
}

function depreciated_deploy() public {
address addr = HuffDeployer.deploy_with_args(
"test/contracts/Constructor",
Expand Down Expand Up @@ -140,7 +167,7 @@ contract HuffDeployerCode {
" sstore // [] \n"
"}";

// Deploy the contract with arguments
// Deploy the contract with arguments (test mode - default)
address addr = HuffDeployer
.config()
.with_args(bytes.concat(abi.encode(uint256(0x420)), abi.encode(uint256(0x420))))
Expand All @@ -155,6 +182,22 @@ contract HuffDeployerCode {
assert(construct.getArgTwo() == uint256(0x420));
}

function deployProduction() public {
// Same constructor macro as above
string memory constructor_macro = "..."; // (truncated for brevity)

// Deploy to production with inline code injection
address addr = HuffDeployer
.config()
.set_broadcast(true) // Enable production deployment
.with_deployer(tx.origin) // Set deployer address
.with_args(bytes.concat(abi.encode(uint256(0x420)), abi.encode(uint256(0x420))))
.with_code(constructor_macro) // Inject constructor code
.deploy("test/contracts/NoConstructor");

// Production deployment completed - verify externally
}

function depreciated_deploy_with_code() public {
address addr = HuffDeployer.deploy_with_code(
"test/contracts/Constructor",
Expand All @@ -165,3 +208,153 @@ contract HuffDeployerCode {
}
}
```

## Production Deployment

The foundry-huff library now supports both test environment simulation and real production network deployment through the broadcast functionality. This enables seamless transition from development to production.

### Test vs Production Mode

The HuffDeployer can operate in two modes:

- **Test Mode** (default): Uses `vm.prank()` to simulate deployment contexts for testing
- **Production Mode**: Uses `vm.broadcast()` to execute real transactions on networks

### Basic Production Deployment

To deploy a Huff contract to a real network, use the `set_broadcast(true)` method:

```solidity
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.13 <0.9.0;

import {Script} from "forge-std/Script.sol";
import {HuffDeployer} from "foundry-huff/HuffDeployer.sol";

contract ProductionDeploy is Script {
function run() public {
// Deploy to production network with broadcast mode
address deployedContract = HuffDeployer
.config()
.set_broadcast(true) // Enable production deployment
.with_deployer(tx.origin) // Set deployer address
.deploy("test/contracts/RememberCreator");

console.log("Contract deployed at:", deployedContract);
}
}
```

### Complete Production Example

Here's a complete example deploying a contract that remembers its creator:

#### 1. Deploy the Contract

Use the provided deployment script to deploy to a local network:

```bash
forge script scripts/Deploy.s.sol --rpc-url localhost:8545 --broadcast --private-key $SINGER_KEY
```

This command will:
- Compile the Huff contract
- Deploy it to the specified network (localhost:8545)
- Use the private key from `$SINGER_KEY` environment variable
- The contract will be deployed with the creator address set to the address corresponding to `$SINGER_KEY`

#### 2. Verify Deployment

After deployment, you'll receive a contract address (e.g., `0xAF194984fa4570B8fE909c102844Bd2584E6E90b`). Verify the deployment:

```bash
# Call the CREATOR() function to verify the creator address
cast call 0xAF194984fa4570B8fE909c102844Bd2584E6E90b "CREATOR() (address)"
```

This should return the address that corresponds to your `$SINGER_KEY`, confirming that the contract correctly stored the deployer's address.

### Deployment with Constructor Arguments

For contracts requiring constructor arguments in production:

```solidity
contract ProductionDeployWithArgs is Script {
function run() public {
address deployedContract = HuffDeployer
.config()
.set_broadcast(true)
.with_deployer(tx.origin)
.with_args(bytes.concat(
abi.encode(address(0x1234567890123456789012345678901234567890)),
abi.encode(uint256(1000))
))
.deploy("test/contracts/Constructor");
}
}
```

### Advanced Configuration

You can combine multiple configuration options for complex deployment scenarios:

```solidity
contract AdvancedProductionDeploy is Script {
function run() public {
// Deploy with custom code injection and arguments
address deployedContract = HuffDeployer
.config()
.set_broadcast(true) // Production mode
.with_deployer(msg.sender) // Custom deployer
.with_value(1 ether) // Deploy with ETH value
.with_args(abi.encode(msg.sender)) // Constructor arguments
.with_code("// Custom constructor code") // Additional code
.deploy("my/contract");
}
}
```

### Security and Best Practices

#### Private Key Management
- **Never hardcode private keys** in your scripts or source code
- Use environment variables: `export SINGER_KEY=0x...`
- Consider using hardware wallets for mainnet deployments
- Use dedicated deployment accounts with limited funds

#### Network Configuration
```bash
# Local development
forge script scripts/Deploy.s.sol --rpc-url localhost:8545 --broadcast --private-key $SINGER_KEY

# Testnet deployment
forge script scripts/Deploy.s.sol --rpc-url https://goerli.infura.io/v3/YOUR_KEY --broadcast --private-key $SINGER_KEY

# Mainnet deployment (use with extreme caution)
forge script scripts/Deploy.s.sol --rpc-url https://mainnet.infura.io/v3/YOUR_KEY --broadcast --private-key $SINGER_KEY
```

#### Deployment Verification
Always verify your deployments:

1. **Check contract address**: Ensure the contract was deployed to the expected address
2. **Verify state**: Call view functions to confirm proper initialization
3. **Test functionality**: Execute test transactions on testnets before mainnet
4. **Gas estimation**: Use `--estimate-gas` flag to preview transaction costs

#### Production Checklist
- [ ] Contract compiled successfully with `huffc`
- [ ] Deployment script tested on local network
- [ ] Private keys securely managed
- [ ] Network RPC URL configured correctly
- [ ] Gas price and limits appropriate for target network
- [ ] Contract verification planned (e.g., Etherscan)
- [ ] Post-deployment testing strategy defined

### Troubleshooting

**Deployment Fails**: Check network connectivity, gas limits, and account balance
**Wrong Creator Address**: Ensure `with_deployer()` is set correctly and private key matches
**Gas Issues**: Huff contracts are gas-optimized, but verify gas limits for complex constructors
**Broadcast Errors**: Confirm `set_broadcast(true)` is called and private key has permissions
```
15 changes: 15 additions & 0 deletions scripts/Deploy.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.13 <0.9.0;

import {Script} from "forge-std/Script.sol";
import {console} from "forge-std/console.sol";
import {HuffDeployer} from "src/HuffDeployer.sol";

contract Deploy is Script {
function run() public {
console.log("Deploying contract with deployer:", tx.origin);
HuffDeployer.config().set_broadcast(true).with_deployer(tx.origin).deploy(
"test/contracts/RememberCreator"
);
}
}
7 changes: 2 additions & 5 deletions src/HuffConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,6 @@ contract HuffConfig {
cleanup[0] = "rm";
cleanup[1] = string.concat("src/", tempFile, ".huff");

// set `msg.sender` for upcoming create context
vm.prank(deployer);


vm.ffi(cleanup);
}

Expand All @@ -232,7 +228,8 @@ contract HuffConfig {

/// @notice deploy the bytecode with the create instruction
address deployedAddress;
if (should_broadcast) vm.broadcast();
if (should_broadcast) vm.broadcast(deployer);
else vm.prank(deployer);
assembly {
let val := sload(value.slot)
deployedAddress := create(val, add(concatenated, 0x20), mload(concatenated))
Expand Down
Loading