Skip to content

Commit 259c36b

Browse files
Merge pull request #30 from BitGo/BG-62483-recovery-contracts
feat(contracts): add contract for recovery
2 parents 783f7e9 + 7af0acf commit 259c36b

3 files changed

Lines changed: 149 additions & 0 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// SPDX-License-Identifier: MIT
2+
// OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol)
3+
pragma solidity 0.8.10;
4+
5+
contract CloneFactory {
6+
7+
function createClone(address implementation) internal returns (address payable instance) {
8+
/// @solidity memory-safe-assembly
9+
assembly {
10+
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
11+
// of the `implementation` address with the bytecode before the address.
12+
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
13+
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
14+
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
15+
instance := create(0, 0x09, 0x37)
16+
}
17+
require(instance != address(0), "ERC1167: create failed");
18+
}
19+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
pragma solidity 0.8.10;
2+
3+
/**
4+
* Basic singleSig contract designed to send funds controlled by signer.
5+
*/
6+
contract RecoveryForwarder {
7+
// Address which can move funds from this contract
8+
address public signer;
9+
/**
10+
* Initialize the signer
11+
*/
12+
function init(address _signer) external onlyUninitialized {
13+
signer = _signer;
14+
}
15+
16+
/**
17+
* Modifier that will execute internal code block only if the sender is an authorized signer on this wallet
18+
*/
19+
modifier onlySigner {
20+
require( signer == msg.sender, 'Non-signer in onlySigner method');
21+
_;
22+
}
23+
24+
/**
25+
* Modifier that will execute internal code block only if the contract has not been initialized yet
26+
*/
27+
modifier onlyUninitialized {
28+
require(signer == address(0x0), 'Already initialized');
29+
_;
30+
}
31+
32+
/**
33+
* Default function; Gets called when Ether is deposited
34+
*/
35+
receive() external payable {
36+
}
37+
38+
/**
39+
* Default function; Gets called when data is sent but does not match any other function
40+
*/
41+
fallback() external payable {
42+
}
43+
44+
/**
45+
* Execute a transaction from this contract using the signer.
46+
*
47+
* @param toAddress the destination address to send an outgoing transaction
48+
* @param value the amount in Wei to be sent
49+
* @param data the data to send to the toAddress when invoking the transaction
50+
*/
51+
function sendFunds(
52+
address toAddress,
53+
uint256 value,
54+
bytes calldata data
55+
) external onlySigner {
56+
// Success, send the transaction
57+
(bool success, ) = toAddress.call{ value: value }(data);
58+
require(success, 'Call execution failed');
59+
}
60+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
pragma solidity 0.8.10;
2+
import "./RecoveryForwarder.sol";
3+
import "./CloneFactory.sol";
4+
/**
5+
*
6+
* RecoveryWallet
7+
* ============
8+
*
9+
* Basic singleSig wallet designed to recover funds.
10+
*
11+
*/
12+
contract RecoveryWallet is CloneFactory {
13+
14+
// Public fields
15+
address public immutable signer;
16+
address public immutable forwarderImplementationAddress;
17+
18+
constructor (address _signer, address _forwarderImplementationAddress) {
19+
signer = _signer;
20+
forwarderImplementationAddress = _forwarderImplementationAddress;
21+
}
22+
/**
23+
* Modifier that will execute internal code block only if the sender is an authorized signer on this wallet
24+
*/
25+
modifier onlySigner {
26+
require( signer == msg.sender, 'Non-signer in onlySigner method');
27+
_;
28+
}
29+
30+
/**
31+
* Gets called when a transaction is received with ether and no data
32+
*/
33+
receive() external payable {
34+
}
35+
/**
36+
* Default function; Gets called when data is sent but does not match any other function
37+
*/
38+
fallback() external payable {
39+
}
40+
41+
/**
42+
* Creates new forwarder contract controlled by signer in batches
43+
*/
44+
function createRecoveryForwarder(uint8 value) external {
45+
require(value > 0 && value < 150 , 'value must be greater than 0 and less than 150');
46+
for ( uint8 i = 0; i < value; ++i) {
47+
address payable clone = createClone(forwarderImplementationAddress);
48+
RecoveryForwarder(clone).init(signer);
49+
}
50+
}
51+
52+
/**
53+
* Execute a transaction from this wallet using the signer.
54+
*
55+
* @param toAddress the destination address to send an outgoing transaction
56+
* @param value the amount in Wei to be sent
57+
* @param data the data to send to the toAddress when invoking the transaction
58+
*/
59+
function sendFunds(
60+
address toAddress,
61+
uint256 value,
62+
bytes calldata data
63+
) external onlySigner {
64+
65+
// Success, send the transaction
66+
(bool success, ) = toAddress.call{ value: value }(data);
67+
require(success, 'Call execution failed');
68+
}
69+
70+
}

0 commit comments

Comments
 (0)