Skip to content

feat(cheatcode): add support for Vyper blueprint #11530

@pyk

Description

@pyk

Component

Forge

Describe the feature you would like

Currently, the vm.deployCode cheatcode works perfectly for deploying standard Vyper contracts. However, it reverts when attempting to deploy a Vyper contract that is meant to be a blueprint (one that would be deployed using create_from_blueprint ).

The ideal solution would be a native, straightforward cheatcode to deploy Vyper blueprint contracts. This could be implemented in one of two ways:

  1. Extend deployCode: Enhance the existing deployCode (and deployCodeTo) cheatcode to accept additional args and correctly handle Vyper blueprint source files.
  2. Add a new cheatcode: Introduce a dedicated cheatcode, such as deployBlueprint(string memory path) or deployBlueprintCode(string memory code), specifically for this purpose.

Either approach would be a significant improvement over the current state.

I have a working solution that uses vm.ffi to call the vyper compiler with the correct flags and then deploys the resulting bytecode using inline assembly.

While this workaround is functional, it's not ideal because it requires the --ffi flag and involves manual, low-level assembly, which is less accessible and more error-prone than a dedicated cheatcode.

/// @notice Deploys a Vyper blueprint contract using ffi to compile and assembly to create.
/// @param path The path to the Vyper source file.
/// @param label A label for the deployed blueprint address in traces.
/// @return blueprint The address of the deployed blueprint contract.
function deployBlueprint(
    string memory path,
    string memory label
) internal returns (address blueprint) {
    // Prepare the command to compile the contract to blueprint bytecode
    string[] memory inputs = new string[](4);
    inputs[0] = "vyper";
    inputs[1] = "-f";
    inputs[2] = "blueprint_bytecode";
    inputs[3] = path;

    // Execute the command via FFI to get the deployment code
    bytes memory bytecode = vm.ffi(inputs);

    // Deploy the bytecode using the CREATE opcode
    assembly {
        // create(value, offset, length)
        blueprint := create(0, add(bytecode, 0x20), mload(bytecode))
    }

    require(blueprint != address(0), "deployBlueprint: Deployment failed");
    vm.label(blueprint, string.concat(label, " Blueprint"));
}

Example usage:

// Deploy the Twocrypto.vy contract as a blueprint
address twocryptoBlueprint = deployBlueprint(
    "contracts/testing/twocrypto/Twocrypto.vy",
    "Twocrypto"
);

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions