Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"@openzeppelin/contracts/token/ERC721/IERC721.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
"
},
"@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.0;
import "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}
"
},
"@openzeppelin/contracts/utils/introspection/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
"
},
"interfaces/IController.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
// Copyright 2025 US Fintech LLC and DelNorte Holdings.
//
// Permission to use, copy, modify, or distribute this software is strictly prohibited
// without prior written consent from both copyright holders.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
// ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// OFFICIAL DEL NORTE NETWORK COMPONENT
// Provides immediate membership access to platform at different levels.
// Required Non US or accredited US registration to swap for DTV token. Registration available within 180 days per terms.delnorte.io .
// Minimally tesed Conroller Tree for world-wide government administration of, well, anything, including property ownership.
// Designed by Ken Silverman as part of his ElasticTreasury (HUB and SPOKE), PeerTreasury and Controller model.
// @author Ken Silverman
// This deployment is for Del Norte Holdings, Delaware and US Fintech, LLC NY.
// Permission to change metadata stored on blockchain explorers and elsewhere granted to:
// Del Norte Holdings, DE only and/or US Fintech, LLC NY independently
pragma solidity 0.8.30;
interface IController {
struct OfficialEntityStruct {
string fullNameOfEntityOrLabel;
string nationalIdOfEntity;
address pubAddress;
uint256 blockNumber;
uint256 blockTimestamp;
bool active;
uint256 value; // Associated value (0-1,000,000 for absolute, or basis points for %, type(uint256).max = no value)
}
struct ChainedEntityStruct {
address entityAddress;
address parent;
uint8 type1; // 0 = 'V' (value/absolute), 1 = 'P' (percentage in basis points)
uint256 val1; // Value for type1 (absolute amount or percentage in basis points)
uint8 type2; // 0 = 'V' (value/absolute), 1 = 'P' (percentage in basis points)
uint256 val2; // Value for type2 (absolute amount or percentage in basis points)
bool active;
uint256 blockNumber;
uint256 blockTimestamp;
string entityName; // Human-readable name
string entityID; // Additional identifier (tax ID, registration #, etc)
}
struct CalculatedEntityAmount {
address entityAddress;
uint256 type1Amount; // Calculated amount for type1 (e.g., transfer fee)
uint256 type2Amount; // Calculated amount for type2 (e.g., activation fee)
}
// Official Entity Functions
function addOfficialEntity(string memory, address, string memory, string memory) external returns (bool);
function addOfficialEntityWithValue(string memory, address, string memory, string memory, uint256) external returns (bool);
function removeOfficialEntity(string memory, address) external returns (bool);
function isOfficialEntity(string memory, address) external view returns (bool);
function isOfficialEntityFast(bytes32, address) external view returns (bool);
function isOfficialDoubleEntityFast(bytes32, address, bytes32, address, bool) external view returns (bool);
function isOfficialTripleEntityFast(bytes32, address, bytes32, address, bytes32, address, bool) external view returns (bool);
function isOfficialQuadrupleEntityFast(bytes32, address, bytes32, address, bytes32, address, bytes32, address, bool) external view returns (bool);
function getOfficialEntity(string calldata, address) external view returns (OfficialEntityStruct memory);
function getAllOfficialEntities(string calldata, bool) external view returns (OfficialEntityStruct[] memory);
function init(address, string calldata) external;
// Chained Entity Functions
function addChainedEntity(string memory, address, address, uint8, uint256, uint8, uint256, string memory, string memory) external returns (bool);
function removeChainedEntity(string calldata, address, bool) external returns (bool);
function isChainedEntity(string calldata, address) external view returns (bool);
function isChainedEntityFast(bytes32, address) external view returns (bool);
function getChainedEntity(string calldata, address) external view returns (ChainedEntityStruct memory);
function getActiveChainedEntities(string calldata) external view returns (ChainedEntityStruct[] memory);
function calculateChainedAmounts(string calldata, uint256, bool, uint256, uint8) external view returns (CalculatedEntityAmount[] memory, uint256, uint256);
// Constants
function BASIS_POINTS_DIVISOR() external view returns (uint256);
}
"
},
"contracts/IssueTokenContentManager.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED\r
// Copyright 2025 US Fintech LLC\r
// \r
// Permission to use, copy, modify, or distribute this software is strictly prohibited\r
// without prior written consent from either copyright holder.\r
// \r
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\r
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY\r
// CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,\r
// ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
\r
pragma solidity 0.8.30;\r
\r
import "../interfaces/IController.sol";\r
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";\r
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";\r
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";\r
\r
/// @title IssueTokenContentManager\r
/// @notice Manages NFT pointers, document hashes, and official strings for IssueToken\r
/// @dev This contract is permanently tied to a single IssueToken instance\r
/// @author Ken Silverman\r
contract IssueTokenContentManager {\r
\r
/* ========== IMMUTABLES ========== */\r
\r
/// @notice The Controller for entity authorization\r
address public immutable controller;\r
\r
/* ========== CONSTANTS ========== */\r
\r
uint256 public constant MAX_STRING_LENGTH = 1024; // Max chars for official strings (gas limit)\r
\r
// NFT ownership status constants\r
uint8 constant NFT_OWNED_NO = 0;\r
uint8 constant NFT_OWNED_YES = 1;\r
uint8 constant NFT_OWNED_UNCHECKED = 2;\r
\r
bytes32 public constant KECCAK_TOKEN_ADMIN = keccak256(bytes("TokenAdmin"));\r
bytes32 public constant KECCAK_TREASURY_ADMIN = keccak256(bytes("TreasuryAdmin"));\r
\r
/* ========== STRUCTS ========== */\r
\r
/// @notice Structure for token info URL history with content hashes\r
struct TokenInfoHistory {\r
string baseUrl;\r
bytes32 contentHash;\r
uint256 timestamp;\r
}\r
\r
/// @notice Structure for NFT transaction records\r
struct NFTTransaction {\r
address nftAddress;\r
uint256 tokenId;\r
uint8 transactionType;\r
uint8 isOwned; // 0=NO, 1=YES, 2=UNCHECKED\r
bool isActive;\r
uint256 blockNumber;\r
uint256 blockTimestamp;\r
}\r
\r
/// @notice Structure for NFT record with full history\r
struct NFTRecord {\r
address nftAddress;\r
bool everAdded;\r
NFTTransaction[] transactions;\r
}\r
\r
/// @notice Structure for official strings (emails, notes, etc.)\r
struct OfficialString {\r
string content;\r
bytes32 contentHash;\r
uint256 timestamp;\r
bool isActive;\r
}\r
\r
/* ========== STORAGE ========== */\r
\r
// Token Info URL & Hash Tracking\r
string public tokenInfoUrl;\r
TokenInfoHistory[] public tokenInfoHistory;\r
mapping(bytes32 => uint256) private contentHashToIndex;\r
\r
// NFT Tracking\r
mapping(address => NFTRecord) private nftRecords;\r
address[] private allNFTAddresses;\r
\r
// Official Strings\r
mapping(bytes32 => OfficialString) private officialStrings;\r
bytes32[] private officialStringHashes;\r
\r
/* ========== EVENTS ========== */\r
\r
// Token Info Events\r
event TokenInfoUrlUpdated(string newUrl, bytes32 indexed contentHash, address indexed updatedBy, uint256 timestamp);\r
\r
// NFT Events\r
event NFTTransactionAdded(address indexed nftAddress, uint256 indexed tokenId, uint8 transactionType, uint8 isOwned, bool isActive, uint256 blockNumber, uint256 blockTimestamp);\r
event NFTOwnershipUpdated(address indexed nftAddress, uint256 indexed tokenId, uint8 oldIsOwned, uint8 newIsOwned);\r
\r
// Official String Events\r
event OfficialStringAdded(bytes32 indexed contentHash, string content, uint256 timestamp);\r
event OfficialStringDeactivated(bytes32 indexed contentHash, uint256 timestamp);\r
\r
/* ========== MODIFIERS ========== */\r
\r
modifier onlyTokenAdmin() {\r
require(IController(controller).isOfficialEntityFast(KECCAK_TOKEN_ADMIN, msg.sender), "Not TokenAdmin");\r
_;\r
}\r
\r
modifier onlyTreasuryAdmin() {\r
require(IController(controller).isOfficialEntityFast(KECCAK_TREASURY_ADMIN, msg.sender), "Not TreasuryAdmin");\r
_;\r
}\r
\r
/* ========== CONSTRUCTOR ========== */\r
\r
/**\r
* @notice Creates a content manager for an IssueToken\r
* @param _controller The Controller address for authorization\r
* @param _tokenInfoBaseUrl Initial token info base URL\r
* @param _tokenInfoHash Initial content hash (SHA-256 of document at URL)\r
*/\r
constructor(\r
address _controller,\r
string memory _tokenInfoBaseUrl,\r
bytes32 _tokenInfoHash\r
) {\r
require(_controller != address(0), "Invalid controller address");\r
require(bytes(_tokenInfoBaseUrl).length > 0, "URL cannot be empty");\r
require(_tokenInfoHash != bytes32(0), "Hash cannot be zero");\r
\r
controller = _controller;\r
tokenInfoUrl = _tokenInfoBaseUrl;\r
\r
// Initialize history with first entry\r
tokenInfoHistory.push(TokenInfoHistory({\r
baseUrl: _tokenInfoBaseUrl,\r
contentHash: _tokenInfoHash,\r
timestamp: block.timestamp\r
}));\r
\r
contentHashToIndex[_tokenInfoHash] = 0;\r
\r
emit TokenInfoUrlUpdated(_tokenInfoBaseUrl, _tokenInfoHash, msg.sender, block.timestamp);\r
}\r
\r
/* ========== TOKEN INFO URL & HASH FUNCTIONS ========== */\r
\r
/**\r
* @notice Updates the token info base URL and adds to immutable history\r
* @param _newTokenInfoBaseUrl New base URL (document must be uploaded first)\r
* @param _newContentHash SHA-256 hash of document at new URL\r
*/\r
function setOfficialTokenInfoBaseUrl(string memory _newTokenInfoBaseUrl, bytes32 _newContentHash) external onlyTreasuryAdmin {\r
require(bytes(_newTokenInfoBaseUrl).length > 0, "URL cannot be empty");\r
require(_newContentHash != bytes32(0), "Hash cannot be zero");\r
\r
tokenInfoUrl = _newTokenInfoBaseUrl;\r
\r
// Add to history\r
uint256 newIndex = tokenInfoHistory.length;\r
tokenInfoHistory.push(TokenInfoHistory({\r
baseUrl: _newTokenInfoBaseUrl,\r
contentHash: _newContentHash,\r
timestamp: block.timestamp\r
}));\r
\r
contentHashToIndex[_newContentHash] = newIndex;\r
\r
emit TokenInfoUrlUpdated(_newTokenInfoBaseUrl, _newContentHash, msg.sender, block.timestamp);\r
}\r
\r
/**\r
* @notice Returns full history of token info URLs and hashes\r
* @return Array of all historical token info entries\r
*/\r
function getBaseUrlHistory() external view returns (TokenInfoHistory[] memory) {\r
return tokenInfoHistory;\r
}\r
\r
/**\r
* @notice Confirms if a content hash exists in history\r
* @param hash The content hash to verify\r
* @return trailNumber Index in history array\r
* @return timeStamp When this hash was added\r
*/\r
function confirmHash(bytes32 hash) external view returns (uint256 trailNumber, uint256 timeStamp) {\r
require(contentHashToIndex[hash] < tokenInfoHistory.length || \r
(contentHashToIndex[hash] == 0 && tokenInfoHistory.length > 0 && tokenInfoHistory[0].contentHash == hash),\r
"Hash not found");\r
\r
uint256 index = contentHashToIndex[hash];\r
TokenInfoHistory memory entry = tokenInfoHistory[index];\r
\r
return (index, entry.timestamp);\r
}\r
\r
/**\r
* @notice Returns the official token information URL with controller address\r
* @dev Constructs full URL as: https://[tokenInfoUrl]/[controller_address]\r
* Handles trailing slashes automatically to prevent double slashes\r
* @return Full URL string including protocol, domain, and the Controller's address\r
* \r
* ⚠️ SECURITY PROTOCOL:\r
* - This URL is the ONLY official source for token offering documents\r
* - Document hash MUST match on-chain hash (verify via confirmHash)\r
* - Domain trustworthiness is buyer's responsibility\r
* - No content at URL = token is NOT VALID\r
* - Content present = domain owner is FULLY responsible\r
*/\r
function getOfficialTokenInfoURL() external view returns (string memory) {\r
bytes memory baseUrlBytes = bytes(tokenInfoUrl);\r
\r
// Check if baseUrl ends with '/' (0x2f in ASCII)\r
bool hasTrailingSlash = baseUrlBytes.length > 0 && baseUrlBytes[baseUrlBytes.length - 1] == 0x2f;\r
\r
if (hasTrailingSlash) {\r
// Base URL already has trailing slash, don't add another\r
return string(abi.encodePacked("https://", tokenInfoUrl, _toHexString(controller)));\r
} else {\r
// Add slash before controller address\r
return string(abi.encodePacked("https://", tokenInfoUrl, "/", _toHexString(controller)));\r
}\r
}\r
\r
/// @notice Internal helper to convert address to hex string (lowercase with 0x prefix)\r
function _toHexString(address addr) internal pure returns (string memory) {\r
bytes memory hexChars = "0123456789abcdef";\r
bytes memory result = new bytes(42);\r
result[0] = '0';\r
result[1] = 'x';\r
for (uint256 i = 0; i < 20; i++) {\r
uint8 b = uint8(uint160(addr) >> (8 * (19 - i)));\r
result[2 + i * 2] = hexChars[b >> 4];\r
result[3 + i * 2] = hexChars[b & 0x0f];\r
}\r
return string(result);\r
}\r
\r
/* ========== NFT MANAGEMENT FUNCTIONS ========== */\r
\r
/**\r
* @notice Add an NFT transaction record\r
* @param nftAddress Address of the NFT contract (must implement ERC721)\r
* @param tokenId Specific token ID (0 = unknown or not applicable)\r
* @param transactionType Transaction type (0-10 defined, 11-255 reserved)\r
* @param isActive Whether this transaction makes the NFT relationship active\r
*/\r
function addNFTTransaction(address nftAddress, uint256 tokenId, uint8 transactionType, bool isActive) external onlyTokenAdmin {\r
require(nftAddress != address(0), "Invalid NFT address");\r
\r
// Verify ERC721 interface\r
try IERC165(nftAddress).supportsInterface(type(IERC721).interfaceId) returns (bool supported) {\r
require(supported, "Not ERC721");\r
} catch {\r
revert("Invalid ERC721 contract");\r
}\r
\r
NFTRecord storage record = nftRecords[nftAddress];\r
\r
if (!record.everAdded) {\r
record.nftAddress = nftAddress;\r
record.everAdded = true;\r
allNFTAddresses.push(nftAddress);\r
}\r
\r
record.transactions.push(NFTTransaction({\r
nftAddress: nftAddress,\r
tokenId: tokenId,\r
transactionType: transactionType,\r
isOwned: NFT_OWNED_UNCHECKED,\r
isActive: isActive,\r
blockNumber: block.number,\r
blockTimestamp: block.timestamp\r
}));\r
\r
emit NFTTransactionAdded(nftAddress, tokenId, transactionType, NFT_OWNED_UNCHECKED, isActive, block.number, block.timestamp);\r
}\r
\r
/**\r
* @notice Get all NFT addresses associated with this token\r
*/\r
function getAllNFTAddresses() external view returns (address[] memory) {\r
return allNFTAddresses;\r
}\r
\r
/**\r
* @notice Get transaction history for a specific NFT\r
*/\r
function getNFTTransactions(address nftAddress) external view returns (NFTTransaction[] memory) {\r
return nftRecords[nftAddress].transactions;\r
}\r
\r
/**\r
* @notice Get current active status of an NFT\r
*/\r
function getNFTStatus(address nftAddress) external view returns (bool isActive, uint8 lastTransactionType) {\r
NFTRecord storage record = nftRecords[nftAddress];\r
if (record.transactions.length == 0) {\r
return (false, 0);\r
}\r
NFTTransaction storage lastTx = record.transactions[record.transactions.length - 1];\r
return (lastTx.isActive, lastTx.transactionType);\r
}\r
\r
/**\r
* @notice Get all currently active NFTs\r
*/\r
function getActiveNFTs() external view returns (address[] memory) {\r
uint256 activeCount = 0;\r
for (uint256 i = 0; i < allNFTAddresses.length; i++) {\r
NFTRecord storage record = nftRecords[allNFTAddresses[i]];\r
if (record.transactions.length > 0) {\r
NFTTransaction storage lastTx = record.transactions[record.transactions.length - 1];\r
if (lastTx.isActive) {\r
activeCount++;\r
}\r
}\r
}\r
\r
address[] memory activeNFTs = new address[](activeCount);\r
uint256 index = 0;\r
for (uint256 i = 0; i < allNFTAddresses.length; i++) {\r
NFTRecord storage record = nftRecords[allNFTAddresses[i]];\r
if (record.transactions.length > 0) {\r
NFTTransaction storage lastTx = record.transactions[record.transactions.length - 1];\r
if (lastTx.isActive) {\r
activeNFTs[index++] = allNFTAddresses[i];\r
}\r
}\r
}\r
\r
return activeNFTs;\r
}\r
\r
/**\r
* @notice Get all NFTs that a token contract actually owns on-chain\r
* @param tokenAddress The token contract to check ownership for\r
* @param updateOwnershipStatus If true, updates all records with actual ownership\r
*/\r
function getOfficiallyOwnedNFTs(address tokenAddress, bool updateOwnershipStatus) external returns (NFTTransaction[] memory ownedNFTs) {\r
require(tokenAddress != address(0), "Invalid token address");\r
if (updateOwnershipStatus) {\r
for (uint256 i = 0; i < allNFTAddresses.length; i++) {\r
address nftContract = allNFTAddresses[i];\r
NFTRecord storage record = nftRecords[nftContract];\r
\r
try IERC721(nftContract).balanceOf(tokenAddress) returns (uint256 balance) {\r
bool actuallyOwned = (balance > 0);\r
\r
for (uint256 j = 0; j < record.transactions.length; j++) {\r
NFTTransaction storage nftTx = record.transactions[j];\r
uint8 oldIsOwned = nftTx.isOwned;\r
uint8 newIsOwned = actuallyOwned ? NFT_OWNED_YES : NFT_OWNED_NO;\r
\r
if (oldIsOwned != newIsOwned) {\r
nftTx.isOwned = newIsOwned;\r
emit NFTOwnershipUpdated(nftContract, nftTx.tokenId, oldIsOwned, newIsOwned);\r
}\r
\r
if (actuallyOwned && nftTx.tokenId == 0) {\r
try IERC165(nftContract).supportsInterface(type(IERC721Enumerable).interfaceId) returns (bool supportsEnumerable) {\r
if (supportsEnumerable && balance > 0) {\r
try IERC721Enumerable(nftContract).tokenOfOwnerByIndex(tokenAddress, 0) returns (uint256 tokenId) {\r
nftTx.tokenId = tokenId;\r
} catch {}\r
}\r
} catch {}\r
}\r
}\r
} catch {\r
for (uint256 j = 0; j < record.transactions.length; j++) {\r
NFTTransaction storage nftTx = record.transactions[j];\r
if (nftTx.isOwned != NFT_OWNED_NO) {\r
uint8 oldIsOwned = nftTx.isOwned;\r
nftTx.isOwned = NFT_OWNED_NO;\r
emit NFTOwnershipUpdated(nftContract, nftTx.tokenId, oldIsOwned, NFT_OWNED_NO);\r
}\r
}\r
}\r
}\r
}\r
\r
// Count owned\r
uint256 ownedCount = 0;\r
for (uint256 i = 0; i < allNFTAddresses.length; i++) {\r
NFTRecord storage record = nftRecords[allNFTAddresses[i]];\r
for (uint256 j = 0; j < record.transactions.length; j++) {\r
if (record.transactions[j].isOwned == NFT_OWNED_YES) {\r
ownedCount++;\r
}\r
}\r
}\r
\r
// Build array\r
ownedNFTs = new NFTTransaction[](ownedCount);\r
uint256 index = 0;\r
for (uint256 i = 0; i < allNFTAddresses.length; i++) {\r
NFTRecord storage record = nftRecords[allNFTAddresses[i]];\r
for (uint256 j = 0; j < record.transactions.length; j++) {\r
if (record.transactions[j].isOwned == NFT_OWNED_YES) {\r
ownedNFTs[index] = record.transactions[j];\r
index++;\r
}\r
}\r
}\r
\r
return ownedNFTs;\r
}\r
\r
/* ========== OFFICIAL STRING FUNCTIONS ========== */\r
\r
/**\r
* @notice Store an official string (email confirmation, note, etc.)\r
* @param content The string to store (max 1024 chars to limit gas)\r
* @dev Gas cost: ~21K base + ~100 gas/char. 1024 chars ≈ 121K gas total\r
*/\r
function addOfficialString(string memory content) external onlyTokenAdmin {\r
require(bytes(content).length > 0 && bytes(content).length <= MAX_STRING_LENGTH, \r
"String must be 1-1024 characters");\r
\r
bytes32 hash = keccak256(bytes(content));\r
require(officialStrings[hash].timestamp == 0, "String already exists");\r
\r
officialStrings[hash] = OfficialString({\r
content: content,\r
contentHash: hash,\r
timestamp: block.timestamp,\r
isActive: true\r
});\r
\r
officialStringHashes.push(hash);\r
emit OfficialStringAdded(hash, content, block.timestamp);\r
}\r
\r
/**\r
* @notice Verify if a string is official and return its details\r
* @param content The string to verify\r
* @return The OfficialString struct (reverts if not found)\r
*/\r
function isOfficialString(string memory content) external view returns (OfficialString memory) {\r
bytes32 hash = keccak256(bytes(content));\r
require(officialStrings[hash].timestamp != 0, "String not found");\r
return officialStrings[hash];\r
}\r
\r
/**\r
* @notice Deactivate an official string (keeps proof it existed)\r
* @param content The string to deactivate\r
*/\r
function makeOfficialStringInactive(string memory content) external onlyTokenAdmin {\r
bytes32 hash = keccak256(bytes(content));\r
require(officialStrings[hash].timestamp != 0, "String not found");\r
officialStrings[hash].isActive = false;\r
emit OfficialStringDeactivated(hash, block.timestamp);\r
}\r
\r
/**\r
* @notice Get all official string hashes\r
*/\r
function getAllOfficialStringHashes() external view returns (bytes32[] memory) {\r
return officialStringHashes;\r
}\r
}\r
\r
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 10000
},
"viaIR": true,
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}
}}
Submitted on: 2025-11-04 10:06:20
Comments
Log in to comment.
No comments yet.