Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ControlProxyWallet contract #151

Open
wants to merge 18 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
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
46 changes: 46 additions & 0 deletions contracts/mock/MockCallProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.7;

import "../interfaces/ICallProxy.sol";

contract MockCallProxy is ICallProxy {
uint256 public override submissionChainIdFrom;
bytes public override submissionNativeSender;

event MockCallProxyCallSuccess();
event MockCallProxyCallFail();

function call(
address,
address _receiver,
bytes memory _data,
uint256,
bytes memory _nativeSender,
uint256 _chainIdFrom
) external payable override returns (bool) {
submissionChainIdFrom = _chainIdFrom;
submissionNativeSender = _nativeSender;
(bool success,) = payable(_receiver).call{value: msg.value}(_data);

if (success) {
emit MockCallProxyCallSuccess();
} else {
emit MockCallProxyCallFail();
}

return success;
}

function callERC20(
address,
address,
address,
bytes memory,
uint256,
bytes memory,
uint256
) external pure override returns (bool) {
//NOP for now
return false;
}
}
7 changes: 7 additions & 0 deletions contracts/mock/MockLogAnyCall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.7;

contract MockAcceptAnyCall {
fallback() payable external {
}
}
174 changes: 174 additions & 0 deletions contracts/periphery/ControlWalletProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.7;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/interfaces/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";

import "../interfaces/ICallProxy.sol";
import "../transfers/DeBridgeGate.sol";

contract ControlWalletProxy is Initializable {
using SafeERC20Upgradeable for IERC20Upgradeable;

/* ========== STATE VARIABLES ========== */

DeBridgeGate public deBridgeGate;
uint256 public controllingAddressesCount;
// chainIdFrom => list of addresses that can control this contract
mapping(uint256 => mapping(bytes => bool)) public controlParams;

uint256 public nonce;

/* ========== ERRORS ========== */

error CallProxyBadRole();
error NativeSenderBadRole(bytes nativeSender, uint256 chainIdFrom);
error ExternalCallFailed();

error AddressAlreadyAdded();
error RemovingMissingAddress();
error RemovingLastAddress();

error WrongNonce();

/* ========== EVENTS ========== */

// emitted when controlling address updated
event ControllingAddressUpdated(
bytes nativeSender,
uint256 chainIdFrom,
bool enabled
);

/* ========== MODIFIERS ========== */

modifier onlyCallProxyFromControllingAddress() {
ICallProxy callProxy = ICallProxy(deBridgeGate.callProxy());
if (address(callProxy) != msg.sender) revert CallProxyBadRole();

bytes memory nativeSender = callProxy.submissionNativeSender();
uint256 chainIdFrom = callProxy.submissionChainIdFrom();
if(!controlParams[chainIdFrom][nativeSender]) {
revert NativeSenderBadRole(nativeSender, chainIdFrom);
}
_;
}

/* ========== CONSTRUCTOR ========== */

function initialize(
DeBridgeGate _deBridgeGate,
bytes memory _nativeSender,
uint256 _chainIdFrom
) public initializer {
deBridgeGate = _deBridgeGate;
controllingAddressesCount++;
controlParams[_chainIdFrom][_nativeSender] = true;
}

function call(
address token,
uint256 amount,
address destination,
bytes memory data,
uint256 _nonce
) external payable onlyCallProxyFromControllingAddress returns (bool _result) {
if (_nonce != nonce){
revert WrongNonce();
}

if (token != address(0)) {
IERC20Upgradeable(token).safeApprove(destination, 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a bool needAprove argument?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the user needs just transfer token from his wallet he doesn't need to create approve

IERC20Upgradeable(token).safeApprove(destination, amount);
}

_result = externalCall(
destination,
amount,
data
);

if (!_result) {
revert ExternalCallFailed();
}

nonce++;
}

function addControllingAddress(
bytes memory _nativeSender,
uint256 _chainIdFrom
) external onlyCallProxyFromControllingAddress {
if(controlParams[_chainIdFrom][_nativeSender]) {
revert AddressAlreadyAdded();
}

controllingAddressesCount++;

controlParams[_chainIdFrom][_nativeSender] = true;

emit ControllingAddressUpdated(_nativeSender, _chainIdFrom, true);
}

function removeControllingAddress(
bytes memory _nativeSender,
uint256 _chainIdFrom
) external onlyCallProxyFromControllingAddress {
if(!controlParams[_chainIdFrom][_nativeSender]) {
revert RemovingMissingAddress();
}

if (controllingAddressesCount == 1){
revert RemovingLastAddress();
}

controllingAddressesCount--;

controlParams[_chainIdFrom][_nativeSender] = false;

emit ControllingAddressUpdated(_nativeSender, _chainIdFrom, false);
}

// ============ VIEWS ============

function isControllingAddress(
bytes memory _nativeSender,
uint256 _chainIdFrom
) external view returns (bool) {
return controlParams[_chainIdFrom][_nativeSender];
}

// ============ INTERNALS ============

//gnosis
//https://github.com/gnosis/MultiSigWallet/blob/ca981359cf5acd6a1f9db18e44777e45027df5e0/contracts/MultiSigWallet.sol#L244-L261

function externalCall(
address destination,
uint256 value,
bytes memory data
) internal returns (bool result) {
uint256 dataLength = data.length;
assembly {
let x := mload(0x40) // "Allocate" memory for output (0x40 is where "free memory" pointer is stored by convention)
let d := add(data, 32) // First 32 bytes are the padded length of data, so exclude that
result := call(
sub(gas(), 34710), // 34710 is the value that solidity is currently emitting
// It includes callGas (700) + callVeryLow (3, to pay for SUB) + callValueTransferGas (9000) +
// callNewAccountGas (25000, in case the destination address does not exist and needs creating)
destination,
value,
d,
dataLength, // Size of the input (in bytes) - this is what fixes the padding problem
x,
0 // Output is ignored, therefore the output size is zero
)
}
}

// ============ Version Control ============
function version() external pure returns (uint256) {
return 101; // 1.0.1
}
}
Loading