Skip to content

Commit

Permalink
feat: inbound evm prototype (#178)
Browse files Browse the repository at this point in the history
Co-authored-by: lumtis <[email protected]>
  • Loading branch information
skosito and lumtis authored Jun 27, 2024
1 parent cb0ca6c commit b9e5bfd
Show file tree
Hide file tree
Showing 48 changed files with 12,512 additions and 3,371 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import "./interfaces.sol";
// This version include a functionality allowing to call a contract
// ERC20Custody doesn't call smart contract directly, it passes through the Gateway contract
contract ERC20CustodyNew {
IGateway public gateway;
IGatewayEVM public gateway;

event Withdraw(address indexed token, address indexed to, uint256 amount);
event WithdrawAndCall(address indexed token, address indexed to, uint256 amount, bytes data);

constructor(address _gateway) {
gateway = IGateway(_gateway);
gateway = IGatewayEVM(_gateway);
}

// Withdraw is called by TSS address, it directly transfers the tokens to the destination address without contract call
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,36 @@ import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";


// The Gateway contract is the endpoint to call smart contracts on external chains
// The GatewayEVM contract is the endpoint to call smart contracts on external chains
// The contract doesn't hold any funds and should never have active allowances
contract Gateway is Initializable, OwnableUpgradeable, UUPSUpgradeable {
contract GatewayEVM is Initializable, OwnableUpgradeable, UUPSUpgradeable {
error ExecutionFailed();
error DepositFailed();
error InsufficientETHAmount();
error InsufficientERC20Amount();
error ZeroAddress();

address public custody;
address public tssAddress;

event Executed(address indexed destination, uint256 value, bytes data);
event ExecutedWithERC20(address indexed token, address indexed to, uint256 amount, bytes data);
event Deposit(address indexed sender, address indexed receiver, uint256 amount, address asset, bytes payload);
event Call(address indexed sender, address indexed receiver, bytes payload);

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function initialize() public initializer {
function initialize(address _tssAddress) public initializer {
__Ownable_init();
__UUPSUpgradeable_init();

if (_tssAddress == address(0)) {
revert ZeroAddress();
}
tssAddress = _tssAddress;
}

function _authorizeUpgrade(address newImplementation) internal override onlyOwner() {}
Expand Down Expand Up @@ -61,6 +73,7 @@ contract Gateway is Initializable, OwnableUpgradeable, UUPSUpgradeable {
bytes calldata data
) external returns (bytes memory) {
// Approve the target contract to spend the tokens
IERC20(token).approve(to, 0);
IERC20(token).approve(to, amount);

// Execute the call on the target contract
Expand All @@ -80,6 +93,51 @@ contract Gateway is Initializable, OwnableUpgradeable, UUPSUpgradeable {
return result;
}

// Deposit ETH to tss
function deposit(address receiver) external payable {
if (msg.value == 0) revert InsufficientETHAmount();
(bool deposited, ) = tssAddress.call{value: msg.value}("");

if (deposited == false) {
revert DepositFailed();
}

emit Deposit(msg.sender, receiver, msg.value, address(0), "");
}

// Deposit ERC20 tokens to custody
function deposit(address receiver, uint256 amount, address asset) external {
if (amount == 0) revert InsufficientERC20Amount();
IERC20(asset).transferFrom(msg.sender, address(custody), amount);

emit Deposit(msg.sender, receiver, amount, asset, "");
}

// Deposit ETH to tss and call an omnichain smart contract
function depositAndCall(address receiver, bytes calldata payload) external payable {
if (msg.value == 0) revert InsufficientETHAmount();
(bool deposited, ) = tssAddress.call{value: msg.value}("");

if (deposited == false) {
revert DepositFailed();
}

emit Deposit(msg.sender, receiver, msg.value, address(0), payload);
}

// Deposit ERC20 tokens to custody and call an omnichain smart contract
function depositAndCall(address receiver, uint256 amount, address asset, bytes calldata payload) external {
if (amount == 0) revert InsufficientERC20Amount();
IERC20(asset).transferFrom(msg.sender, address(custody), amount);

emit Deposit(msg.sender, receiver, amount, asset, payload);
}

// Call an omnichain smart contract without asset transfer
function call(address receiver, bytes calldata payload) external {
emit Call(msg.sender, receiver, payload);
}

function setCustody(address _custody) external {
custody = _custody;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,36 @@ import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";


// NOTE: Purpose of this contract is to test upgrade process, the only difference should be event names
// NOTE: Purpose of this contract is to test upgrade process, the only difference should be name of Executed event
// The Gateway contract is the endpoint to call smart contracts on external chains
// The contract doesn't hold any funds and should never have active allowances
contract GatewayUpgradeTest is Initializable, OwnableUpgradeable, UUPSUpgradeable {
contract GatewayEVMUpgradeTest is Initializable, OwnableUpgradeable, UUPSUpgradeable {
error ExecutionFailed();
error SendFailed();
error InsufficientETHAmount();
error ZeroAddress();

address public custody;
address public tssAddress;

event ExecutedV2(address indexed destination, uint256 value, bytes data);
event ExecutedWithERC20V2(address indexed token, address indexed to, uint256 amount, bytes data);
event ExecutedWithERC20(address indexed token, address indexed to, uint256 amount, bytes data);
event SendERC20(bytes recipient, address indexed asset, uint256 amount);
event Send(bytes recipient, uint256 amount);

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function initialize() public initializer {
function initialize(address _tssAddress) public initializer {
__Ownable_init();
__UUPSUpgradeable_init();

if (_tssAddress == address(0)) {
revert ZeroAddress();
}
tssAddress = _tssAddress;
}

function _authorizeUpgrade(address newImplementation) internal override onlyOwner() {}
Expand Down Expand Up @@ -62,6 +73,7 @@ contract GatewayUpgradeTest is Initializable, OwnableUpgradeable, UUPSUpgradeabl
bytes calldata data
) external returns (bytes memory) {
// Approve the target contract to spend the tokens
IERC20(token).approve(to, 0);
IERC20(token).approve(to, amount);

// Execute the call on the target contract
Expand All @@ -76,11 +88,33 @@ contract GatewayUpgradeTest is Initializable, OwnableUpgradeable, UUPSUpgradeabl
IERC20(token).transfer(address(custody), remainingBalance);
}

emit ExecutedWithERC20V2(token, to, amount, data);
emit ExecutedWithERC20(token, to, amount, data);

return result;
}

// Transfer specified token amount to ERC20Custody and emits event
function sendERC20(bytes calldata recipient, address token, uint256 amount) external {
IERC20(token).transferFrom(msg.sender, address(custody), amount);

emit SendERC20(recipient, token, amount);
}

// Transfer specified ETH amount to TSS address and emits event
function send(bytes calldata recipient, uint256 amount) external payable {
if (msg.value == 0) {
revert InsufficientETHAmount();
}

(bool sent, ) = tssAddress.call{value: msg.value}("");

if (sent == false) {
revert SendFailed();
}

emit Send(recipient, msg.value);
}

function setCustody(address _custody) external {
custody = _custody;
}
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;

interface IGateway {
interface IGatewayEVM {
function executeWithERC20(
address token,
address to,
Expand All @@ -10,4 +10,8 @@ interface IGateway {
) external returns (bytes memory);

function execute(address destination, bytes calldata data) external payable returns (bytes memory);

function sendERC20(bytes calldata recipient, address asset, uint256 amount) external;

function send(bytes calldata recipient, uint256 amount) external payable;
}
Loading

0 comments on commit b9e5bfd

Please sign in to comment.