1616
1717pragma solidity ^ 0.8.24 ;
1818
19+ import { ERC721 } from "openzeppelin-contracts/contracts/token/ERC721/ERC721.sol " ;
20+
1921interface GemLike {
2022 function transferFrom (address from , address to , uint256 amount ) external ;
2123 function transfer (address to , uint256 amount ) external ;
@@ -25,19 +27,10 @@ interface IdentityNetworkLike {
2527 function isMember (address account ) external view returns (bool );
2628}
2729
28- interface ERC721ReceiverLike {
29- function onERC721Received (
30- address operator ,
31- address from ,
32- uint256 tokenId ,
33- bytes calldata data
34- ) external returns (bytes4 );
35- }
36-
3730/// @title NFATFacility
3831/// @notice Non-Fungible Allocation Token Facility for bespoke capital deployment deals
3932/// @dev Implements queue-based deposits and ERC-721 NFAT minting
40- contract NFATFacility {
33+ contract NFATFacility is ERC721 {
4134
4235 // --- Immutables ---
4336
@@ -62,13 +55,7 @@ contract NFATFacility {
6255
6356 // --- NFAT Storage ---
6457
65- string public name;
66- string public symbol;
6758 uint256 public nextTokenId;
68- mapping (uint256 tokenId = > address owner ) internal _owners;
69- mapping (address owner = > uint256 count ) internal _balances;
70- mapping (uint256 tokenId = > address approved ) internal _tokenApprovals;
71- mapping (address owner = > mapping (address operator = > bool approved )) internal _operatorApprovals;
7259
7360 // --- Events: Access Control ---
7461
@@ -93,12 +80,6 @@ contract NFATFacility {
9380 event Fund (uint256 indexed tokenId , address indexed funder , uint256 amount );
9481 event Redeem (uint256 indexed tokenId , uint256 amount );
9582
96- // --- Events: ERC-721 ---
97-
98- event Transfer (address indexed from , address indexed to , uint256 indexed tokenId );
99- event Approval (address indexed owner , address indexed approved , uint256 indexed tokenId );
100- event ApprovalForAll (address indexed owner , address indexed operator , bool approved );
101-
10283 // --- Modifiers ---
10384
10485 modifier auth () {
@@ -123,11 +104,11 @@ contract NFATFacility {
123104
124105 // --- Constructor ---
125106
126- constructor (address gem_ , address almProxy_ , string memory name_ , string memory symbol_ ) {
107+ constructor (address gem_ , address almProxy_ , string memory name_ , string memory symbol_ )
108+ ERC721 (name_, symbol_)
109+ {
127110 gem = GemLike (gem_);
128111 almProxy = almProxy_;
129- name = name_;
130- symbol = symbol_;
131112 wards[msg .sender ] = 1 ;
132113 emit Rely (msg .sender );
133114 }
@@ -218,22 +199,18 @@ contract NFATFacility {
218199 require (amount > 0 , "NFATFacility/zero-amount " );
219200 require (deposits[target] >= amount, "NFATFacility/insufficient-deposits " );
220201
221- require (identityNetwork == address (0 ) || IdentityNetworkLike (identityNetwork).isMember (target), "NFATFacility/target-not-member " );
222-
223202 uint256 tokenId = nextTokenId++ ;
224203
225204 // Effects - Queue
226205 unchecked { deposits[target] -= amount; }
227206
228- // Effects - NFAT
229- _owners[tokenId] = target;
230- _balances[target] += 1 ;
207+ // Effects - NFAT (identity network check in _update)
208+ _mint (target, tokenId);
231209
232210 // Interactions
233211 gem.transfer (almProxy, amount);
234212
235213 emit Issue (target, tokenId, amount);
236- emit Transfer (address (0 ), target, tokenId);
237214 }
238215
239216 // --- Redeem Functions ---
@@ -242,7 +219,7 @@ contract NFATFacility {
242219 /// @param tokenId The NFAT to fund
243220 /// @param amount The amount of gem to deposit
244221 function fund (uint256 tokenId , uint256 amount ) external {
245- require (_owners[ tokenId] != address (0 ), "NFATFacility/invalid-token " );
222+ require (_ownerOf ( tokenId) != address (0 ), "NFATFacility/invalid-token " );
246223 require (amount > 0 , "NFATFacility/zero-amount " );
247224
248225 // Effects
@@ -261,7 +238,7 @@ contract NFATFacility {
261238 require (amount > 0 , "NFATFacility/zero-amount " );
262239 require (funded[tokenId] >= amount, "NFATFacility/insufficient-funded " );
263240
264- address owner = _owners[ tokenId] ;
241+ address owner = _ownerOf ( tokenId) ;
265242 require (msg .sender == owner, "NFATFacility/not-owner " );
266243 require (identityNetwork == address (0 ) || IdentityNetworkLike (identityNetwork).isMember (owner), "NFATFacility/not-member " );
267244
@@ -274,89 +251,15 @@ contract NFATFacility {
274251 emit Redeem (tokenId, amount);
275252 }
276253
277- // --- ERC-721 Functions ---
278-
279- function ownerOf (uint256 tokenId ) public view returns (address ) {
280- address owner = _owners[tokenId];
281- require (owner != address (0 ), "NFATFacility/invalid-token " );
282- return owner;
283- }
284-
285- function balanceOf (address owner ) external view returns (uint256 ) {
286- require (owner != address (0 ), "NFATFacility/zero-address " );
287- return _balances[owner];
288- }
289-
290- function approve (address to , uint256 tokenId ) external {
291- address owner = ownerOf (tokenId);
292- require (msg .sender == owner || _operatorApprovals[owner][msg .sender ], "NFATFacility/not-authorized " );
293- _tokenApprovals[tokenId] = to;
294- emit Approval (owner, to, tokenId);
295- }
296-
297- function getApproved (uint256 tokenId ) external view returns (address ) {
298- require (_owners[tokenId] != address (0 ), "NFATFacility/invalid-token " );
299- return _tokenApprovals[tokenId];
300- }
301-
302- function setApprovalForAll (address operator , bool approved ) external {
303- require (operator != msg .sender , "NFATFacility/self-approval " );
304- _operatorApprovals[msg .sender ][operator] = approved;
305- emit ApprovalForAll (msg .sender , operator, approved);
306- }
307-
308- function isApprovedForAll (address owner , address operator ) external view returns (bool ) {
309- return _operatorApprovals[owner][operator];
310- }
311-
312- function transferFrom (address from , address to , uint256 tokenId ) public {
313- require (_isApprovedOrOwner (msg .sender , tokenId), "NFATFacility/not-authorized " );
314- require (ownerOf (tokenId) == from, "NFATFacility/wrong-from " );
315- require (to != address (0 ), "NFATFacility/zero-address " );
316-
317- require (identityNetwork == address (0 ) || IdentityNetworkLike (identityNetwork).isMember (to), "NFATFacility/to-not-member " );
318-
319- // Clear approval
320- _tokenApprovals[tokenId] = address (0 );
321-
322- // Effects
323- _balances[from] -= 1 ;
324- _balances[to] += 1 ;
325- _owners[tokenId] = to;
326-
327- emit Transfer (from, to, tokenId);
328- }
329-
330- function safeTransferFrom (address from , address to , uint256 tokenId ) external {
331- safeTransferFrom (from, to, tokenId, "" );
332- }
333-
334- function safeTransferFrom (address from , address to , uint256 tokenId , bytes memory data ) public {
335- transferFrom (from, to, tokenId);
336- require (_checkOnERC721Received (from, to, tokenId, data), "NFATFacility/unsafe-recipient " );
337- }
338-
339- function _isApprovedOrOwner (address spender , uint256 tokenId ) internal view returns (bool ) {
340- address owner = ownerOf (tokenId);
341- return (spender == owner || _tokenApprovals[tokenId] == spender || _operatorApprovals[owner][spender]);
342- }
343-
344- function _checkOnERC721Received (address from , address to , uint256 tokenId , bytes memory data ) internal returns (bool ) {
345- if (to.code.length == 0 ) {
346- return true ;
347- }
348- try ERC721ReceiverLike (to).onERC721Received (msg .sender , from, tokenId, data) returns (bytes4 retval ) {
349- return retval == ERC721ReceiverLike .onERC721Received.selector ;
350- } catch {
351- return false ;
352- }
353- }
354-
355- // --- ERC-165 ---
254+ // --- ERC-721 Overrides ---
356255
357- function supportsInterface (bytes4 interfaceId ) external pure returns (bool ) {
358- return
359- interfaceId == 0x01ffc9a7 || // ERC-165
360- interfaceId == 0x80ac58cd ; // ERC-721
256+ /// @dev OZ's _mint and transferFrom both revert before calling _update when to == address(0),
257+ /// and _burn is never invoked, so `to` is guaranteed to be non-zero here.
258+ function _update (address to , uint256 tokenId , address auth_ ) internal override returns (address ) {
259+ require (
260+ identityNetwork == address (0 ) || IdentityNetworkLike (identityNetwork).isMember (to),
261+ "NFATFacility/not-member "
262+ );
263+ return super ._update (to, tokenId, auth_);
361264 }
362265}
0 commit comments