Contract Name:
SocketDeployFactory
Contract Source Code:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "../utils/Ownable.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {ISocketBridgeBase} from "../interfaces/ISocketBridgeBase.sol";
/**
* @dev In the constructor, set up the initialization code for socket
* contracts as well as the keccak256 hash of the given initialization code.
* that will be used to deploy any transient contracts, which will deploy any
* socket contracts that require the use of a constructor.
*
* Socket contract initialization code (29 bytes):
*
* 0x5860208158601c335a63aaf10f428752fa158151803b80938091923cf3
*
* Description:
*
* pc|op|name | [stack] | <memory>
*
* ** set the first stack item to zero - used later **
* 00 58 getpc [0] <>
*
* ** set second stack item to 32, length of word returned from staticcall **
* 01 60 push1
* 02 20 outsize [0, 32] <>
*
* ** set third stack item to 0, position of word returned from staticcall **
* 03 81 dup2 [0, 32, 0] <>
*
* ** set fourth stack item to 4, length of selector given to staticcall **
* 04 58 getpc [0, 32, 0, 4] <>
*
* ** set fifth stack item to 28, position of selector given to staticcall **
* 05 60 push1
* 06 1c inpos [0, 32, 0, 4, 28] <>
*
* ** set the sixth stack item to msg.sender, target address for staticcall **
* 07 33 caller [0, 32, 0, 4, 28, caller] <>
*
* ** set the seventh stack item to msg.gas, gas to forward for staticcall **
* 08 5a gas [0, 32, 0, 4, 28, caller, gas] <>
*
* ** set the eighth stack item to selector, "what" to store via mstore **
* 09 63 push4
* 10 aaf10f42 selector [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42] <>
*
* ** set the ninth stack item to 0, "where" to store via mstore ***
* 11 87 dup8 [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42, 0] <>
*
* ** call mstore, consume 8 and 9 from the stack, place selector in memory **
* 12 52 mstore [0, 32, 0, 4, 0, caller, gas] <0xaaf10f42>
*
* ** call staticcall, consume items 2 through 7, place address in memory **
* 13 fa staticcall [0, 1 (if successful)] <address>
*
* ** flip success bit in second stack item to set to 0 **
* 14 15 iszero [0, 0] <address>
*
* ** push a third 0 to the stack, position of address in memory **
* 15 81 dup2 [0, 0, 0] <address>
*
* ** place address from position in memory onto third stack item **
* 16 51 mload [0, 0, address] <>
*
* ** place address to fourth stack item for extcodesize to consume **
* 17 80 dup1 [0, 0, address, address] <>
*
* ** get extcodesize on fourth stack item for extcodecopy **
* 18 3b extcodesize [0, 0, address, size] <>
*
* ** dup and swap size for use by return at end of init code **
* 19 80 dup1 [0, 0, address, size, size] <>
* 20 93 swap4 [size, 0, address, size, 0] <>
*
* ** push code position 0 to stack and reorder stack items for extcodecopy **
* 21 80 dup1 [size, 0, address, size, 0, 0] <>
* 22 91 swap2 [size, 0, address, 0, 0, size] <>
* 23 92 swap3 [size, 0, size, 0, 0, address] <>
*
* ** call extcodecopy, consume four items, clone runtime code to memory **
* 24 3c extcodecopy [size, 0] <code>
*
* ** return to deploy final code in memory **
* 25 f3 return [] *deployed!*
*/
contract SocketDeployFactory is Ownable {
using SafeTransferLib for ERC20;
address public immutable disabledRouteAddress;
mapping(address => address) _implementations;
mapping(uint256 => bool) isDisabled;
mapping(uint256 => bool) isRouteDeployed;
mapping(address => bool) canDisableRoute;
event Deployed(address _addr);
event DisabledRoute(address _addr);
event Destroyed(address _addr);
error ContractAlreadyDeployed();
error NothingToDestroy();
error AlreadyDisabled();
error CannotBeDisabled();
error OnlyDisabler();
constructor(address _owner, address disabledRoute) Ownable(_owner) {
disabledRouteAddress = disabledRoute;
canDisableRoute[_owner] = true;
}
modifier onlyDisabler() {
if (!canDisableRoute[msg.sender]) {
revert OnlyDisabler();
}
_;
}
function addDisablerAddress(address disabler) external onlyOwner {
canDisableRoute[disabler] = true;
}
function removeDisablerAddress(address disabler) external onlyOwner {
canDisableRoute[disabler] = false;
}
/**
* @notice Deploys a route contract at predetermined location
* @notice Caller must first deploy the route contract at another location and pass its address as implementation.
* @param routeId route identifier
* @param implementationContract address of deployed route contract. Its byte code will be copied to predetermined location.
*/
function deploy(
uint256 routeId,
address implementationContract
) external onlyOwner returns (address) {
// assign the initialization code for the socket contract.
bytes memory initCode = (
hex"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3"
);
// determine the address of the socket contract.
address routeContractAddress = _getContractAddress(routeId);
if (isRouteDeployed[routeId]) {
revert ContractAlreadyDeployed();
}
isRouteDeployed[routeId] = true;
//first we deploy the code we want to deploy on a separate address
// store the implementation to be retrieved by the socket contract.
_implementations[routeContractAddress] = implementationContract;
address addr;
assembly {
let encoded_data := add(0x20, initCode) // load initialization code.
let encoded_size := mload(initCode) // load init code's length.
addr := create2(0, encoded_data, encoded_size, routeId) // routeId is used as salt
}
require(
addr == routeContractAddress,
"Failed to deploy the new socket contract."
);
emit Deployed(addr);
return addr;
}
/**
* @notice Destroy the route deployed at a location.
* @param routeId route identifier to be destroyed.
*/
function destroy(uint256 routeId) external onlyDisabler {
// determine the address of the socket contract.
_destroy(routeId);
}
/**
* @notice Deploy a disabled contract at destroyed route to handle it gracefully.
* @param routeId route identifier to be disabled.
*/
function disableRoute(
uint256 routeId
) external onlyDisabler returns (address) {
return _disableRoute(routeId);
}
/**
* @notice Destroy a list of routeIds
* @param routeIds array of routeIds to be destroyed.
*/
function multiDestroy(uint256[] calldata routeIds) external onlyDisabler {
for (uint32 index = 0; index < routeIds.length; ) {
_destroy(routeIds[index]);
unchecked {
++index;
}
}
}
/**
* @notice Deploy a disabled contract at list of routeIds.
* @param routeIds array of routeIds to be disabled.
*/
function multiDisableRoute(
uint256[] calldata routeIds
) external onlyDisabler {
for (uint32 index = 0; index < routeIds.length; ) {
_disableRoute(routeIds[index]);
unchecked {
++index;
}
}
}
/**
* @dev External view function for calculating a socket contract address
* given a particular routeId.
*/
function getContractAddress(
uint256 routeId
) external view returns (address) {
// determine the address of the socket contract.
return _getContractAddress(routeId);
}
//those two functions are getting called by the socket Contract
function getImplementation()
external
view
returns (address implementation)
{
return _implementations[msg.sender];
}
function _disableRoute(uint256 routeId) internal returns (address) {
// assign the initialization code for the socket contract.
bytes memory initCode = (
hex"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3"
);
// determine the address of the socket contract.
address routeContractAddress = _getContractAddress(routeId);
if (!isRouteDeployed[routeId]) {
revert CannotBeDisabled();
}
if (isDisabled[routeId]) {
revert AlreadyDisabled();
}
isDisabled[routeId] = true;
//first we deploy the code we want to deploy on a separate address
// store the implementation to be retrieved by the socket contract.
_implementations[routeContractAddress] = disabledRouteAddress;
address addr;
assembly {
let encoded_data := add(0x20, initCode) // load initialization code.
let encoded_size := mload(initCode) // load init code's length.
addr := create2(0, encoded_data, encoded_size, routeId) // routeId is used as salt.
}
require(
addr == routeContractAddress,
"Failed to deploy the new socket contract."
);
emit Deployed(addr);
return addr;
}
function _destroy(uint256 routeId) internal {
// determine the address of the socket contract.
address routeContractAddress = _getContractAddress(routeId);
if (!isRouteDeployed[routeId]) {
revert NothingToDestroy();
}
ISocketBridgeBase(routeContractAddress).killme();
emit Destroyed(routeContractAddress);
}
/**
* @dev Internal view function for calculating a socket contract address
* given a particular routeId.
*/
function _getContractAddress(
uint256 routeId
) internal view returns (address) {
// determine the address of the socket contract.
bytes memory initCode = (
hex"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3"
);
return
address(
uint160( // downcast to match the address type.
uint256( // convert to uint to truncate upper digits.
keccak256( // compute the CREATE2 hash using 4 inputs.
abi.encodePacked( // pack all inputs to the hash together.
hex"ff", // start with 0xff to distinguish from RLP.
address(this), // this contract will be the caller.
routeId, // the routeId is used as salt.
keccak256(abi.encodePacked(initCode)) // the init code hash.
)
)
)
)
);
}
/**
* @notice Rescues the ERC20 token to an address
this is a restricted function to be called by only socketGatewayOwner
* @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address
* @param token address of the ERC20 token being rescued
* @param userAddress address to which ERC20 is to be rescued
* @param amount amount of ERC20 tokens being rescued
*/
function rescueFunds(
address token,
address userAddress,
uint256 amount
) external onlyOwner {
ERC20(token).safeTransfer(userAddress, amount);
}
/**
* @notice Rescues the native balance to an address
this is a restricted function to be called by only socketGatewayOwner
* @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address
* @param userAddress address to which native-balance is to be rescued
* @param amount amount of native-balance being rescued
*/
function rescueEther(
address payable userAddress,
uint256 amount
) external onlyOwner {
userAddress.call{value: amount}("");
}
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.4;
import {OnlyOwner, OnlyNominee} from "../errors/SocketErrors.sol";
abstract contract Ownable {
address private _owner;
address private _nominee;
event OwnerNominated(address indexed nominee);
event OwnerClaimed(address indexed claimer);
constructor(address owner_) {
_claimOwner(owner_);
}
modifier onlyOwner() {
if (msg.sender != _owner) {
revert OnlyOwner();
}
_;
}
function owner() public view returns (address) {
return _owner;
}
function nominee() public view returns (address) {
return _nominee;
}
function nominateOwner(address nominee_) external {
if (msg.sender != _owner) {
revert OnlyOwner();
}
_nominee = nominee_;
emit OwnerNominated(_nominee);
}
function claimOwner() external {
if (msg.sender != _nominee) {
revert OnlyNominee();
}
_claimOwner(msg.sender);
}
function _claimOwner(address claimer_) internal {
_owner = claimer_;
_nominee = address(0);
emit OwnerClaimed(claimer_);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
interface ISocketBridgeBase {
function killme() external;
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
success := call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "APPROVE_FAILED");
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
error CelerRefundNotReady();
error OnlySocketDeployer();
error OnlySocketGatewayOwner();
error OnlySocketGateway();
error OnlyOwner();
error OnlyNominee();
error TransferIdExists();
error TransferIdDoesnotExist();
error Address0Provided();
error SwapFailed();
error UnsupportedInterfaceId();
error InvalidCelerRefund();
error CelerAlreadyRefunded();
error IncorrectBridgeRatios();
error ZeroAddressNotAllowed();
error ArrayLengthMismatch();
error PartialSwapsNotAllowed();