Skip to content

Instantly share code, notes, and snippets.

@yurenju
Created January 6, 2022 07:52
Show Gist options
  • Save yurenju/ef4c901a48c523ac74bf942b50ab5108 to your computer and use it in GitHub Desktop.
Save yurenju/ef4c901a48c523ac74bf942b50ab5108 to your computer and use it in GitHub Desktop.
OpenZeppelin BeaconProxy
pragma solidity 0.8.7;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract Ship is Initializable {
string public name;
uint256 public fuel;
function initialize(string memory _name, uint256 _fuel) external initializer {
name = _name;
fuel = _fuel;
}
function move() public {
require(fuel > 0, "no feul");
fuel--;
}
}
pragma solidity 0.8.7;
import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract ShipBeacon is Ownable {
UpgradeableBeacon immutable beacon;
address public blueprint;
constructor(address _initBlueprint) {
beacon = new UpgradeableBeacon(_initBlueprint);
blueprint = _initBlueprint;
transferOwnership(tx.origin);
}
function update(address _newBlueprint) public onlyOwner {
beacon.upgradeTo(_newBlueprint);
blueprint = _newBlueprint;
}
function implementation() public view returns (address){
return beacon.implementation();
}
}
pragma solidity 0.8.7;
import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import "./ShipBeacon.sol";
import "./Ship.sol";
contract ShipFactory {
mapping(uint32 => address) private ships;
ShipBeacon immutable beacon;
constructor(address _initBlueprint) {
beacon = new ShipBeacon(_initBlueprint);
}
function buildShip(string memory _name, uint256 _fuel, uint32 shipId) public {
BeaconProxy ship = new BeaconProxy(
address(beacon),
abi.encodeWithSelector(Ship(address(0)).initialize.selector, _name, _fuel)
);
ships[shipId] = address(ship);
}
function getShipAddress(uint32 shipId) external view returns (address) {
return ships[shipId];
}
function getBeacon() public view returns (address) {
return address(beacon);
}
function getImplementation() public view returns (address) {
return beacon.implementation();
}
}
pragma solidity 0.8.7;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract ShipV2 is Initializable {
string public name;
uint256 public fuel;
function initialize(string memory _name, uint256 _fuel) external initializer {
name = _name;
fuel = _fuel;
}
function move() public {
require(fuel > 0, "no feul");
fuel--;
}
function refuel() public {
fuel++;
}
}
@yurenju
Copy link
Author

yurenju commented Jan 6, 2022

BeaconProxy

@allurco
Copy link

allurco commented May 13, 2022

Hey @yurenju, your solution looks very clean, but I have a question: If I wanted to call a ship method in buildShip after the deployment, what should I write? ship.move() is not working.

@yurenju
Copy link
Author

yurenju commented May 21, 2022

@allurco I push all source code with hardhat configuration to BeaconShip repository and modify buildShip() with move() for your reference.

@allurco
Copy link

allurco commented May 21, 2022

@yurenju thank you, so its the proxy that needs to be invoked. Very clear now.

@yurenju
Copy link
Author

yurenju commented May 21, 2022

👍

@chubidursel
Copy link

Thnks! Awesome explanation!
I am deploying almost the same contract in Hardhat, and keep getting the same Error: VM Exception while processing transaction: reverted with reason string 'Address: low-level delegate call failed'
I have change the create func in Solidity tons of time and deployment code, and still have no clue how can i fix this

@yurenju
Copy link
Author

yurenju commented Mar 8, 2023

@chubidursel few things you can try:

  1. clone BeaconShip can deploy to see if any error, if not you can modify from the source code
  2. use tenderly to execute your function to trace the stack

@chubidursel
Copy link

@yurenju Thanks for the advice
Im deploying via hardhat. Maybe my deployment code is broken. I just copied your code and trynna deploy it in lockhost, but it gets err in last function where we create new ship contract. Deployment code below:

async function main() {

const Contract = await ethers.getContractFactory("Ship");

const beacon = await upgrades.deployBeacon(Contract);
await beacon.deployed();

console.log("beacon deployed: ", beacon.address);

const Factory = await ethers.getContractFactory("ShipFactory");
const factory = await Factory.deploy(beacon.address);

console.log("Factory deployed: ", factory.address);

await factory.buildShip("Bob", 10, 1) // ERR > 'Address: low-level delegate call failed'
}

@yurenju
Copy link
Author

yurenju commented Mar 10, 2023

@chubidursel I have no idea about the error. you also can use a separated terminal and execute hardhat console, and execute your deployment code again to see if the terminal running hardhat console print more error details.

@mikaelaakko
Copy link

@chubidursel your error occurs because you are deploying the beacon twice and initializing the beacon deployed in the ShipFactory constructor with your own beacon. With the example code there is no need to deploy the beacon before deploying ShipFactory (because it is done in the constructor), instead, just deploy the Ship and pass that as an argument to the ShipFactory and the error should go away :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment