Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cargo Stylus Factory #240

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
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
123 changes: 123 additions & 0 deletions src/stylus/CargoStylusFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

interface IArbWasm {
function activateProgram(
address program
) external payable returns (uint16 version, uint256 dataFee);
}
Comment on lines +4 to +8
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should import from ./src/precompiles/ArbWasm.sol


contract CargoStylusFactory {
Copy link
Member

@gzeoneth gzeoneth Aug 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing a payable receive function to receive refund excess fund provided for activation

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

specifically, I mean if dataFee < _activationValue in

(, uint256 dataFee) = IArbWasm(ARB_WASM).activateProgram{value: _activationValue}(
            _contract
        );

I am not sure if that refund involves evm call tho

// Constants
address constant ARB_WASM = 0x0000000000000000000000000000000000000071;

// Events
event ContractDeployed(address indexed deployedContract, address indexed deployer);
event ContractActivated(address indexed activatedContract);
event ContractInitialized(address indexed initializedContract);

// Errors
error ContractDeploymentError(bytes bytecode);
error ContractInitializationError(address newContract);
error RefundExcessValueError(uint256 excessValue);
error ExecutionFailed(address target, bytes data);

// Deploys, activate and inits a new contract
function deployActivateInit(
bytes calldata _bytecode,
bytes calldata _constructorCalldata,
uint256 _constructorValue
) public payable returns (address) {
uint256 activationValue = msg.value - _constructorValue;

// Deploy the contract
address newContractAddress = deployContract(_bytecode);

// Activate the contract
uint256 activationFee = activateContract(newContractAddress, activationValue);

// Initialize the contract
initializeContract(newContractAddress, _constructorCalldata, _constructorValue);

refundExcessValue(activationFee, _constructorValue);
Comment on lines +40 to +42
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this ignore any refund from the constructor call (if any), I think we can wrap a balance check before and after the call to constructor to know the net amount spent in the constructor and do refund appropriately

there is also an additional edge case where the constructor cost is negative (if it somehow received more fund from the constructor)


return newContractAddress;
}

// Function to deploy and init a new contract
// NOTE: This function should only be invoked if
// ArbWasm's codehashVersion(bytes32 codehash) method returns the
// current stylusVersion(), verify this locally before invoking
function deployInit(
Comment on lines +47 to +51
Copy link
Member

@gzeoneth gzeoneth Aug 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of having a separate method, we can just conditional activate based on the result of codehashVersion in the other method
can also conditional on if the activation cost provided is 0

bytes calldata _bytecode,
bytes calldata _constructorCalldata
) public payable returns (address) {
// Deploy the contract
address newContractAddress = deployContract(_bytecode);

// Initialize the contract
initializeContract(newContractAddress, _constructorCalldata, msg.value);

return newContractAddress;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

excess value not refunded (some constructor might refund callvalue)

}

// Computes amount to refund and sends it to msg.sender
function refundExcessValue(uint256 _activationFee, uint256 _constructorValue) internal {
// Calculate value to forward to constructor
uint256 excessValue = msg.value - _activationFee - _constructorValue;

// Refund excess value
(bool sent, ) = payable(msg.sender).call{value: excessValue}("");

if (!sent) {
revert RefundExcessValueError(excessValue);
}
}

// Internal function to deploy a new contract
function deployContract(bytes memory _bytecode) internal returns (address) {
address newContractAddress;

assembly {
newContractAddress := create(0, add(_bytecode, 0x20), mload(_bytecode))
}

if (newContractAddress == address(0)) {
revert ContractDeploymentError(_bytecode);
}

emit ContractDeployed(newContractAddress, msg.sender);

return newContractAddress;
}

// Internal function to activate a Stylus contract
function activateContract(
address _contract,
uint256 _activationValue
) internal returns (uint256) {
(, uint256 dataFee) = IArbWasm(ARB_WASM).activateProgram{value: _activationValue}(
_contract
);

emit ContractActivated(_contract);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm getting this error if the program was previously activated:

    (code: 3, message: execution reverted: error ProgramUpToDate(), data: Some(String("0xcc944bf2")))


return dataFee;
}
Comment on lines +95 to +106

// Internal function to initialize a Stylus contract by
// invoking its constructor
function initializeContract(
Comment on lines +78 to +110
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we were to consolidate the 2 public function, we might as well inline these instead of making internal functions

address _contract,
bytes calldata _constructorCalldata,
uint256 _constructorValue
) internal {
(bool success, ) = address(_contract).call{value: _constructorValue}(_constructorCalldata);

if (!success) {
revert ContractInitializationError(_contract);
}

emit ContractInitialized(_contract);
}
}
Loading