Hi Ed,
I created the following simple Minimal Proxy Factory to compute an address and deploy.
I used @tinchoabbate’s excellent deep dive to get the byte code for a minimal proxy:
I used OpenZeppelin Contracts Create2 to compute the address and to deploy.
The following code hasn’t been tested nor has it been audited
A more complete version would also include initialization of the minimal proxy
(e.g. https://github.com/OpenZeppelin/openzeppelin-sdk/blob/master/packages/lib/contracts/upgradeability/ProxyFactory.sol#L32)
MinimalProxyFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "@openzeppelin/contracts/utils/Create2.sol";
contract MinimalProxyFactory {
event MinimalProxyCreated(address minimalProxy);
function computeAddress(uint256 salt, address implementation)
public
view
returns (address)
{
return
Create2.computeAddress(
keccak256(abi.encodePacked(salt)),
keccak256(getContractCreationCode(implementation)),
address(this)
);
}
function deploy(
uint256 salt,
address implementation
) public {
address minimalProxy = Create2.deploy(
0,
keccak256(abi.encodePacked(salt)),
getContractCreationCode(implementation)
);
emit MinimalProxyCreated(minimalProxy);
}
function getContractCreationCode(address logic)
internal
pure
returns (bytes memory)
{
bytes10 creation = 0x3d602d80600a3d3981f3;
bytes10 prefix = 0x363d3d373d3d3d363d73;
bytes20 targetBytes = bytes20(logic);
bytes15 suffix = 0x5af43d82803e903d91602b57fd5bf3;
return abi.encodePacked(creation, prefix, targetBytes, suffix);
}
}
Box.sol
From: https://docs.openzeppelin.com/learn/developing-smart-contracts#setting-up-a-solidity-project
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract Box {
uint256 private value;
// Emitted when the stored value changes
event ValueChanged(uint256 newValue);
// Stores a new value in the contract
function store(uint256 newValue) public {
value = newValue;
emit ValueChanged(newValue);
}
// Reads the last stored value
function retrieve() public view returns (uint256) {
return value;
}
}
2_deploy.js
// migrations/2_deploy.js
const Box = artifacts.require("Box");
const MinimalProxyFactory = artifacts.require("MinimalProxyFactory");
module.exports = async function (deployer) {
await deployer.deploy(Box);
await deployer.deploy(MinimalProxyFactory);
};
Deploy a minimal proxy
Using the factory, I compute an address, then deploy a minimal proxy and interact with it.
$ npx truffle console --network rinkeby
truffle(rinkeby)> box = await Box.deployed()
truffle(rinkeby)> factory = await MinimalProxyFactory.deployed()
truffle(rinkeby)> await factory.computeAddress(42, box.address)
'0xFa4f101C260ebFDdf98f6b3e0A159886D876Fe0B'
truffle(rinkeby)> await factory.deploy(42, box.address)
{ tx:
'0x301f328227120e90c90bbb3ab8c23672e4cb310e3519842b1ecad29ccbed5036',
...
truffle(rinkeby)> boxProxy = await Box.at('0xFa4f101C260ebFDdf98f6b3e0A159886D876Fe0B')
truffle(rinkeby)> await boxProxy.store(7)
{ tx:
'0x9db5a64b67281b903114e90a488440ece6a3f3a3485c676884990484d5e5ba61',
...
truffle(rinkeby)> (await boxProxy.retrieve()).toString()
'7'