Skip to content
Open
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
277 changes: 197 additions & 80 deletions evm/script/helpers/Proxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,77 @@ import { StakingRewardDistributor } from "src/StakingRewardDistributor.sol";
import { MockERC20 } from "test/mocks/MockERC20.sol";
import { LockedTokenStaker } from "src/LockedTokenStaker.sol";

// Events for deployment tracking
event ContractDeployed(string contractType, address implementation, address proxy, bytes32 salt);

// Errors
error ZeroAddress();
error EmptyIdentifier();
error DeploymentFailed();

/**
* @dev Internal helper function to deploy proxy contracts
* @param impl Implementation contract address
* @param initialOwner Owner of the proxy admin
* @param initData Initialization calldata
* @param salt Salt for CREATE2 deployment
* @return proxy The deployed proxy address
*/
function _deployProxy(
address impl,
address initialOwner,
bytes memory initData,
bytes32 salt
)
returns (address proxy)
{
if (impl == address(0) || initialOwner == address(0)) revert ZeroAddress();

// Use different salt for proxy to avoid collision
bytes32 proxySalt = keccak256(abi.encodePacked(salt, "proxy"));

TransparentUpgradeableProxy deployedProxy = new TransparentUpgradeableProxy{ salt: proxySalt }({
_logic: impl,
initialOwner: initialOwner,
_data: initData
});

proxy = address(deployedProxy);
if (proxy == address(0)) revert DeploymentFailed();
}

function newWCT(address initialOwner, WCT.Init memory init) returns (WCT) {
if (initialOwner == address(0)) revert ZeroAddress();

bytes32 salt = keccak256(abi.encodePacked("walletconnect.wct"));

WCT impl = new WCT{ salt: salt }();
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy{ salt: salt }({
_logic: address(impl),
initialOwner: address(initialOwner),
_data: abi.encodeCall(WCT.initialize, init)
});

return WCT(address(proxy));
address proxy = _deployProxy(
address(impl),
initialOwner,
abi.encodeCall(WCT.initialize, init),
salt
);

emit ContractDeployed("WCT", address(impl), proxy, salt);
return WCT(proxy);
}

function newL2WCT(address initialOwner, LegacyL2WCT.Init memory init) returns (LegacyL2WCT) {
if (initialOwner == address(0)) revert ZeroAddress();

bytes32 salt = keccak256(abi.encodePacked("walletconnect.l2wct"));

LegacyL2WCT impl = new LegacyL2WCT{ salt: salt }();
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy{ salt: salt }({
_logic: address(impl),
initialOwner: address(initialOwner),
_data: abi.encodeCall(LegacyL2WCT.initialize, init)
});

return LegacyL2WCT(address(proxy));
address proxy = _deployProxy(
address(impl),
initialOwner,
abi.encodeCall(LegacyL2WCT.initialize, init),
salt
);

emit ContractDeployed("LegacyL2WCT", address(impl), proxy, salt);
return LegacyL2WCT(proxy);
}

function newWalletConnectConfig(
Expand All @@ -46,55 +93,71 @@ function newWalletConnectConfig(
)
returns (WalletConnectConfig)
{
if (initialOwner == address(0)) revert ZeroAddress();

bytes32 salt = keccak256(abi.encodePacked("walletconnect.config"));

WalletConnectConfig impl = new WalletConnectConfig{ salt: salt }();
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy{ salt: salt }({
_logic: address(impl),
initialOwner: address(initialOwner),
_data: abi.encodeCall(WalletConnectConfig.initialize, init)
});

return WalletConnectConfig(address(proxy));
address proxy = _deployProxy(
address(impl),
initialOwner,
abi.encodeCall(WalletConnectConfig.initialize, init),
salt
);

emit ContractDeployed("WalletConnectConfig", address(impl), proxy, salt);
return WalletConnectConfig(proxy);
}

function newPauser(address initialOwner, Pauser.Init memory init) returns (Pauser) {
if (initialOwner == address(0)) revert ZeroAddress();

bytes32 salt = keccak256(abi.encodePacked("walletconnect.pauser"));

Pauser impl = new Pauser{ salt: salt }();
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy{ salt: salt }({
_logic: address(impl),
initialOwner: address(initialOwner),
_data: abi.encodeCall(Pauser.initialize, init)
});

return Pauser(address(proxy));
address proxy = _deployProxy(
address(impl),
initialOwner,
abi.encodeCall(Pauser.initialize, init),
salt
);

emit ContractDeployed("Pauser", address(impl), proxy, salt);
return Pauser(proxy);
}

function newRewardManager(address initialOwner, RewardManager.Init memory init) returns (RewardManager) {
if (initialOwner == address(0)) revert ZeroAddress();

bytes32 salt = keccak256(abi.encodePacked("walletconnect.rewardmanager"));

RewardManager impl = new RewardManager{ salt: salt }();
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy{ salt: salt }({
_logic: address(impl),
initialOwner: address(initialOwner),
_data: abi.encodeCall(RewardManager.initialize, init)
});

return RewardManager(address(proxy));
address proxy = _deployProxy(
address(impl),
initialOwner,
abi.encodeCall(RewardManager.initialize, init),
salt
);

emit ContractDeployed("RewardManager", address(impl), proxy, salt);
return RewardManager(proxy);
}

function newStaking(address initialOwner, Staking.Init memory init) returns (Staking) {
if (initialOwner == address(0)) revert ZeroAddress();

bytes32 salt = keccak256(abi.encodePacked("walletconnect.staking"));

Staking impl = new Staking{ salt: salt }();
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy{ salt: salt }({
_logic: address(impl),
initialOwner: address(initialOwner),
_data: abi.encodeCall(Staking.initialize, init)
});

return Staking(address(proxy));
address proxy = _deployProxy(
address(impl),
initialOwner,
abi.encodeCall(Staking.initialize, init),
salt
);

emit ContractDeployed("Staking", address(impl), proxy, salt);
return Staking(proxy);
}

function newStakingRewardDistributor(
Expand All @@ -103,29 +166,37 @@ function newStakingRewardDistributor(
)
returns (StakingRewardDistributor)
{
if (initialOwner == address(0)) revert ZeroAddress();

bytes32 salt = keccak256(abi.encodePacked("walletconnect.stakingrewarddistributor"));

StakingRewardDistributor impl = new StakingRewardDistributor{ salt: salt }();
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy{ salt: salt }({
_logic: address(impl),
initialOwner: address(initialOwner),
_data: abi.encodeCall(StakingRewardDistributor.initialize, init)
});

return StakingRewardDistributor(address(proxy));
address proxy = _deployProxy(
address(impl),
initialOwner,
abi.encodeCall(StakingRewardDistributor.initialize, init),
salt
);

emit ContractDeployed("StakingRewardDistributor", address(impl), proxy, salt);
return StakingRewardDistributor(proxy);
}

function newStakeWeight(address initialOwner, StakeWeight.Init memory init) returns (StakeWeight) {
if (initialOwner == address(0)) revert ZeroAddress();

bytes32 salt = keccak256(abi.encodePacked("walletconnect.stakeweight"));

StakeWeight impl = new StakeWeight{ salt: salt }();
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy{ salt: salt }({
_logic: address(impl),
initialOwner: address(initialOwner),
_data: abi.encodeCall(StakeWeight.initialize, init)
});

return StakeWeight(address(proxy));
address proxy = _deployProxy(
address(impl),
initialOwner,
abi.encodeCall(StakeWeight.initialize, init),
salt
);

emit ContractDeployed("StakeWeight", address(impl), proxy, salt);
return StakeWeight(proxy);
}

function newLockedTokenStaker(
Expand All @@ -135,28 +206,74 @@ function newLockedTokenStaker(
)
returns (LockedTokenStaker)
{
require(bytes(identifier).length > 0, "Identifier cannot be empty");
if (initialOwner == address(0)) revert ZeroAddress();
if (bytes(identifier).length == 0) revert EmptyIdentifier();

// Validate identifier contains only alphanumeric and allowed special chars
bytes memory identifierBytes = bytes(identifier);
for (uint256 i = 0; i < identifierBytes.length; i++) {
bytes1 char = identifierBytes[i];
bool isValid = (char >= 0x30 && char <= 0x39) || // 0-9
(char >= 0x41 && char <= 0x5A) || // A-Z
(char >= 0x61 && char <= 0x7A) || // a-z
char == 0x2D || char == 0x5F; // - or _
require(isValid, "Invalid identifier character");
}

bytes32 salt = keccak256(abi.encodePacked("walletconnect.lockedtokenstaker.", identifier));

LockedTokenStaker impl = new LockedTokenStaker{ salt: salt }();
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy{ salt: salt }({
_logic: address(impl),
initialOwner: address(initialOwner),
_data: abi.encodeCall(LockedTokenStaker.initialize, init)
});

return LockedTokenStaker(address(proxy));
address proxy = _deployProxy(
address(impl),
initialOwner,
abi.encodeCall(LockedTokenStaker.initialize, init),
salt
);

emit ContractDeployed(
string(abi.encodePacked("LockedTokenStaker-", identifier)),
address(impl),
proxy,
salt
);
return LockedTokenStaker(proxy);
}

function newMockERC20(address initialOwner) returns (MockERC20) {
if (initialOwner == address(0)) revert ZeroAddress();

bytes32 salt = keccak256(abi.encodePacked("walletconnect.mockerc20"));

MockERC20 impl = new MockERC20{ salt: salt }();
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy{ salt: salt }({
_logic: address(impl),
initialOwner: address(initialOwner),
_data: abi.encodeCall(MockERC20.initialize, ())
});
address proxy = _deployProxy(
address(impl),
initialOwner,
abi.encodeCall(MockERC20.initialize, ()),
salt
);

emit ContractDeployed("MockERC20", address(impl), proxy, salt);
return MockERC20(proxy);
}

return MockERC20(address(proxy));
/**
* @dev Helper function to predict deployment addresses before actual deployment
* @param deployer Address that will deploy the contract
* @param saltString Salt string used in deployment
* @return implAddress Predicted implementation address
* @return proxyAddress Predicted proxy address
*/
function predictAddresses(
address deployer,
string memory saltString
)
pure
returns (address implAddress, address proxyAddress)
{
bytes32 salt = keccak256(abi.encodePacked(saltString));
bytes32 proxySalt = keccak256(abi.encodePacked(salt, "proxy"));

// Note: This is simplified - actual prediction requires bytecode hash
// In practice, you'd use a library like CREATE2 address predictor
return (address(0), address(0)); // Placeholder
}