Skip to content

Commit

Permalink
Initial commit simple trust
Browse files Browse the repository at this point in the history
  • Loading branch information
Agusx1211 committed Oct 20, 2023
1 parent e0c5382 commit 17091b5
Show file tree
Hide file tree
Showing 3 changed files with 599 additions and 0 deletions.
160 changes: 160 additions & 0 deletions contracts/trust/Trust.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.18;

import "../interfaces/IERC1271Wallet.sol";
import "../utils/SignatureValidator.sol";

function absDiff(uint256 a, uint256 b) pure returns (bool, uint256) {
if (a > b) {
return (true, a - b);
}

return (false, b - a);
}

contract Trust is IERC1271Wallet {
error UnlockInThePast(uint256 _unlocksAt, uint256 _elapsed);
error UnlockTooEarly(uint256 _unlocksAt, uint256 _diff);

error NotOwner(address _sender);
error NotUnlocked(uint256 _unlocksAt);
error FailedTransaction(address payable _to, uint256 _value, bytes _data, bytes _result);

error EmptySignature();
error InvalidSignatureFlag(bytes _signature, bytes1 _flag);
error InvalidSignature(bytes32 _hash, bytes32 _rehash, address _signer, bytes _signature);

event SetUnlocksAt(uint256 _unlocksAt);
event SentTransaction(address payable _to, uint256 _value, bytes _data, bytes _result);

address immutable public owner;
address immutable public beneficiary;
uint256 immutable public duration;

uint256 public unlocksAt = type(uint256).max;

constructor (
address _owner,
address _beneficiary,
uint256 _duration
) {
owner = _owner;
beneficiary = _beneficiary;
duration = _duration;
}

modifier onlyAllowed() {
if (msg.sender != owner) {
if (msg.sender != beneficiary) {
revert NotOwner(msg.sender);
}

if (isLocked()) {
revert NotUnlocked(unlocksAt);
}
}

_;
}

modifier onlyMember() {
if (msg.sender != owner && msg.sender != beneficiary) {
revert NotOwner(msg.sender);
}

_;
}

function isLocked() public view returns (bool) {
return block.timestamp < unlocksAt;
}

function setUnlocksAt(uint256 _unlocksAt) external onlyMember {
// Diff between the current time and the unlock time must be
// greater than the duration of the trust
(bool isPast, uint256 elapsed) = absDiff(block.timestamp, _unlocksAt);
if (isPast) {
revert UnlockInThePast(_unlocksAt, elapsed);
}

if (elapsed < duration) {
revert UnlockTooEarly(_unlocksAt, elapsed);
}

emit SetUnlocksAt(_unlocksAt);
unlocksAt = _unlocksAt;
}

function sendTransaction(
address payable _to,
uint256 _value,
bytes calldata _data
) external onlyAllowed returns (bytes memory) {
(bool success, bytes memory result) = _to.call{value: _value}(_data);

if (!success) {
revert FailedTransaction(_to, _value, _data, result);
}

emit SentTransaction(_to, _value, _data, result);
return result;
}

bytes4 internal constant SELECTOR_ERC1271_BYTES_BYTES = 0x20c13b0b;
bytes4 internal constant SELECTOR_ERC1271_BYTES32_BYTES = 0x1626ba7e;

function isValidSignature(
bytes calldata _data,
bytes calldata _signature
) external view returns (bytes4) {
bytes4 res = Trust(payable(address((this)))).isValidSignature(
keccak256(_data),
_signature
);

if (res == SELECTOR_ERC1271_BYTES32_BYTES) {
return SELECTOR_ERC1271_BYTES_BYTES;
}

return bytes4(0);
}

function isValidSignature(
bytes32 _hash,
bytes calldata _signature
) external view returns (bytes4) {
if (_signature.length == 0) {
revert EmptySignature();
}

// If last byte is 0x00 we use the owner
// if 0x01 we use the beneficiary
address signer;

if (_signature[_signature.length - 1] == 0x00) {
signer = owner;
} else if (_signature[_signature.length - 1] == 0x01) {
signer = beneficiary;
} else {
revert InvalidSignatureFlag(_signature, _signature[_signature.length - 1]);
}

if (signer != owner && isLocked()) {
revert NotUnlocked(unlocksAt);
}

// Re-hash the hash adding the address of the trust
// otherwise the signature will be valid for any trust
bytes32 rehash = keccak256(abi.encodePacked(address(this), _hash));

// Validate the signature
if (!SignatureValidator.isValidSignature(rehash, signer, _signature[0:_signature.length - 1])) {
revert InvalidSignature(_hash, rehash, signer, _signature[0:_signature.length - 1]);
}

return SELECTOR_ERC1271_BYTES32_BYTES;
}

receive() external payable {}
fallback() external payable {}
}
39 changes: 39 additions & 0 deletions contracts/trust/TrustFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.18;

import "./Trust.sol";


contract TrustFactory {
error ErrorDeployingTrust(address _owner, address _beneficiary, uint256 _duration);

function addressOf(
address _owner,
address _beneficiary,
uint256 _duration
) external view returns (address) {
return address(uint160(uint(keccak256(abi.encodePacked(
bytes1(0xff),
address(this),
bytes32(0),
keccak256(abi.encodePacked(
type(Trust).creationCode,
abi.encode(_owner, _beneficiary, _duration)
))
)))));
}

function deploy(
address _owner,
address _beneficiary,
uint256 _duration
) external returns (address) {
Trust trust = new Trust{ salt: bytes32(0) }( _owner, _beneficiary, _duration);

if (address(trust) == address(0)) {
revert ErrorDeployingTrust(_owner, _beneficiary, _duration);
}

return address(trust);
}
}
Loading

0 comments on commit 17091b5

Please sign in to comment.