-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathEscrow.sol
146 lines (96 loc) · 4.41 KB
/
Escrow.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
// todo: re-entrancy guard
// Check if user has approved Escrow before burning tokens.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
// Escrow contract will store money
// EscrowNFT will store details of individual escrows as NFTs
// Users will be able to transfer NFTs between one another
// ERC721Burnable lets us destroy used/redeemed NFTs
// ERC721Enumerable makes our NFTs countable (enumerable)
// Ownable implements ownership in our contracts
contract EscrowNFT is ERC721Burnable, ERC721Enumerable, Ownable {
uint256 public tokenCounter = 0;
// NFT data
mapping(uint256 => uint256) public amount;
mapping(uint256 => uint256) public matureTime;
constructor() ERC721("EscrowNFT", "ESCRW") {
}
function mint(address _recipient, uint256 _amount, uint256 _matureTime) public onlyOwner returns (uint256) {
_mint(_recipient, tokenCounter);
// set values
amount[tokenCounter] = _amount;
matureTime[tokenCounter] = _matureTime;
// increment counter
tokenCounter++;
return tokenCounter - 1;
}
function tokenDetails(uint256 _tokenId) public view returns (uint256, uint256) {
require(_exists(_tokenId), "EscrowNFT: Query for nonexistent token");
return (amount[_tokenId], matureTime[_tokenId]);
}
function contractAddress() public view returns (address) {
return address(this);
}
function _beforeTokenTransfer(address _from, address _to, uint256 _amount) internal override(ERC721, ERC721Enumerable) { }
function supportsInterface(bytes4 _interfaceId) public view virtual override(ERC721, ERC721Enumerable) returns (bool) { }
}
contract Escrow is Ownable {
EscrowNFT public escrowNFT;
bool public initialized = false;
event Escrowed(address _from, address _to, uint256 _amount, uint256 _matureTime);
event Redeemed(address _recipient, uint256 _amount);
event Initialized(address _escrowNft);
modifier isInitialized() {
require(initialized, "Contract is not yet initialized");
_;
}
function initialize(address _escrowNftAddress) external onlyOwner {
require(!initialized, "Contract already initialized.");
escrowNFT = EscrowNFT(_escrowNftAddress);
initialized = true;
emit Initialized(_escrowNftAddress);
}
function escrowEth(address _recipient, uint256 _duration) external payable isInitialized {
require(_recipient != address(0), "Cannot escrow to zero address.");
require(msg.value > 0, "Cannot escrow 0 ETH.");
uint256 amount = msg.value;
uint256 matureTime = block.timestamp + _duration;
escrowNFT.mint(_recipient, amount, matureTime);
emit Escrowed(msg.sender,
_recipient,
amount,
matureTime);
}
function redeemEthFromEscrow(uint256 _tokenId) external isInitialized {
require(escrowNFT.ownerOf(_tokenId) == msg.sender, "Must own token to claim underlying Eth");
(uint256 amount, uint256 matureTime) = escrowNFT.tokenDetails(_tokenId);
require(matureTime <= block.timestamp, "Escrow period not expired.");
escrowNFT.burn(_tokenId);
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed.");
emit Redeemed(msg.sender, amount);
}
function redeemAllAvailableEth() external isInitialized {
uint256 nftBalance = escrowNFT.balanceOf(msg.sender);
require(nftBalance > 0, "No EscrowNFTs to redeem.");
uint256 totalAmount = 0;
for (uint256 i = 0; i < nftBalance; i++) {
uint256 tokenId = escrowNFT.tokenOfOwnerByIndex(msg.sender, i);
(uint256 amount, uint256 matureTime) = escrowNFT.tokenDetails(tokenId);
if (matureTime <= block.timestamp) {
escrowNFT.burn(tokenId);
totalAmount += amount;
}
}
require(totalAmount > 0, "No Ether to redeem.");
(bool success, ) = msg.sender.call{value: totalAmount}("");
require(success, "Transfer failed.");
emit Redeemed(msg.sender, totalAmount);
}
function contractAddress() public view returns (address) {
return address(this);
}
}