Security
This page describes the security measures in NFT Loans and our approach to keeping your funds safe.
Security Architecture
Defense in Depth
Multiple layers of security protect users:
Layer 1: Smart Contract Design
├── Reentrancy guards on all external calls
├── Checks-effects-interactions pattern
├── Access control on privileged functions
└── Input validation
Layer 2: Safe Integration
├── Guard blocks locked NFT transfers
├── Module limited to liquidation only
├── Setup verification before loans
└── 24-hour activation delay
Layer 3: Economic Security (Deposit Mode)
├── 20% deposit requirement
├── Cheating detection and deposit payout to lender
└── Keeper and community monitoring
Layer 4: Admin Controls
├── 24-hour timelock on sensitive functions
├── Secure owner wallet required for operations
├── No emergency withdrawal of user funds
└── Pausable for emergencies
Layer 5: Compliance Gate
├── Server-side wallet sanctions screening during onboarding
├── Optional legal/business/counterparty name screening
├── Screening records stored in private service-role tables
└── Manual review for non-clear name resultsOnboarding Compliance Screening
Wallet onboarding is gated by a server-side ScreenLayer check. The app calls ScreenLayer from the Next.js API route only, using SCREENLAYER_API_KEY; this key must never be exposed with a NEXT_PUBLIC_ prefix or used in browser code.
The onboarding screening route requires a fresh wallet signature over the screened wallet, customerRef, request origin, and issued-at timestamp before it calls ScreenLayer or writes compliance records. Existing customerRef rows can only be updated by the same wallet address, so a client cannot rebind another onboarding record to a different wallet.
Onboarding can continue only when wallet screening returns clear. Blocked wallet results create a compliance review case and stop onboarding. Name results that require review hold onboarding for manual review and are not treated as a final legal determination.
Smart Contract Security
Reentrancy Protection
All external calls use OpenZeppelin's ReentrancyGuard. Functions that transfer ETH or interact with external contracts are protected.
Access Control
onlyOwner: Admin functionsonlyOwnerOrTimelock: Sensitive admin functions requiring timelockonlyLendingCore: Functions only callable by the core contract
Safe Math
Solidity 0.8+ provides built-in overflow/underflow protection.
No Upgradability
Contracts are NOT upgradeable. This prevents:
- Admin changing contract logic
- Proxy-related vulnerabilities
- Storage collision issues
The tradeoff: bugs cannot be easily fixed. We rely on thorough testing.
Timelock
Sensitive admin functions must go through the timelock:
| Function | Timelock Required | Delay |
|---|---|---|
| Set platform fee | Yes | 24 hours |
| Set collateral guard | Yes | 24 hours |
| Set liquidation module | Yes | 24 hours |
| Set deposit manager | Yes | 24 hours |
| Pause/Unpause | No | Immediate |
How Timelock Works
- Admin queues a transaction
- 24-hour waiting period
- Anyone can monitor queued transactions
- Admin executes after delay
- 14-day grace period to execute
This gives the community time to react to malicious changes.
Safe Guard Security
What the Guard Checks
Every transaction from a protected Safe is inspected:
Transaction
↓
Is it to an NFT contract?
├── No → Allow
└── Yes ↓
Is it a transfer/approve/burn?
├── No → Allow
└── Yes ↓
Is the token locked?
├── No → Allow
└── Yes → BLOCKWhat the Guard Blocks
transferFromfor locked tokenssafeTransferFromfor locked tokensapprovefor locked tokenssetApprovalForAllwhen any tokens are lockedburnfor locked tokens- Removing the guard while loans active
- Disabling the module while loans active
Pre-Loan Checks
Before creating a Safe Guard loan:
- Guard must be installed
- Liquidation Module must be enabled
- No other modules allowed
- No existing approvals on the NFT
Liquidation Module Security
Limited Scope
The module can ONLY:
- Transfer a specific NFT
- To a specific recipient
- When called by TDHLendingCore
- When loan is validly defaulted
Cannot
- Transfer other assets
- Execute arbitrary calls
- Be called by anyone except LendingCore
- Act before default conditions met
Verification
// Module checks before executing
require(msg.sender == lendingCore, "Only lending core");
require(!liquidationExecuted[loanId], "Already executed");
// LendingCore checks before calling
require(loan.status == ACTIVE, "Loan not active");
require(isDefaulted(loanId), "Not defaulted");24-Hour Activation Delay
Safe Guard loans have a waiting period after creation:
Purpose
- Detect if NFT was transferred before loan setup
- Catch setup manipulation attempts
- Give time to cancel suspicious loans
What's Checked at Activation
- Borrower still owns the NFT
- NFT is still locked in the guard
- No bypass detected
Cancellation
Anyone can cancel a pending loan if:
- Borrower no longer owns the NFT
- Lock was somehow bypassed
Lender's funds are returned on cancellation.
Deposit Mode Security
Economic Security Model
The 20% deposit makes cheating costly but not impossible:
Cheating profit = Borrowed ETH + NFT value - DepositIf NFT is worth more than 5x the deposit, cheating is profitable. This is why:
- Deposit Mode has higher interest rates
- Lenders should price this risk
- Safe Guard Mode is recommended for larger loans
Cheating Detection
Anyone can report cheating via reportCheating(loanId):
- Checks if borrower owns NFT
- Checks if approval is valid
- Pays the escrowed deposit to the lender if cheating is detected
Automated Keeper Monitoring
The services package includes keeper monitoring for activation, liquidation, and Deposit mode cheating checks. This is helpful but not a guarantee. Lenders should still:
- Monitor their loans
- Understand that anyone can call the public keeper functions
- Accept the delayed detection risk if keepers or RPC providers are down
Known Limitations
setApprovalForAll
We can check getApproved(tokenId) but cannot enumerate all operators from isApprovedForAll. Users must revoke these manually before borrowing.
Pre-existing Modules
We check that the Safe has exactly one module (ours), but we rely on the user having removed others. A determined attacker with Safe admin access could potentially add modules between checks and loan creation.
Deposit Sizing
The 20% deposit is based on principal, not NFT value. If NFT floor is much higher than principal, cheating economics favor the borrower.
Oracle Centralization
Additional collateral floor prices are admin-set, not oracle-fed. This requires trust in the admin.
Audit Status
| Auditor | Date | Status | Report |
|---|---|---|---|
| Pending | - | - | - |
We recommend waiting for audit completion before depositing significant funds.
Bug Bounty
We're planning a bug bounty program. Details coming soon.
In the meantime, please report security issues through the private GitHub advisory form: https://github.com/maybehotcarl/nftloans/security/advisories/new
Responsible Disclosure
- Do not exploit vulnerabilities
- Give us reasonable time to fix
- Do not disclose publicly until fixed
Incident Response
If a Vulnerability is Found
- Pause the protocol (if needed)
- Assess impact
- Communicate with users
- Deploy fix (if possible) or mitigate
- Post-mortem
Emergency Contacts
- GitHub Security Advisory: https://github.com/maybehotcarl/nftloans/security/advisories/new
- GitHub Issues: https://github.com/maybehotcarl/nftloans/issues
Security Checklist for Users
Before Using
- [ ] Verify contract addresses match docs
- [ ] Check contracts are verified on Etherscan
- [ ] Understand the risks (read this page!)
- [ ] Start with small amounts
For Borrowers (Safe Guard)
- [ ] Use a dedicated Safe for borrowing
- [ ] Remove other modules before setup
- [ ] Revoke existing NFT approvals
- [ ] Verify guard and module are installed
For Borrowers (Deposit)
- [ ] Understand deposit is at risk
- [ ] Don't transfer the NFT
- [ ] Don't revoke approval
- [ ] Repay on time
For Lenders
- [ ] Prefer Safe Guard Mode
- [ ] Don't over-concentrate in one loan
- [ ] Monitor Deposit mode loans
- [ ] Understand NFT liquidation risk
Code Review
The source code is available at: [GitHub link]
Key files to review:
src/core/TDHLendingCore.sol- Main lending logicsrc/guards/TDHCollateralGuard.sol- Safe Guard implementationsrc/modules/TDHLiquidationModule.sol- Safe Module for liquidationsrc/core/TDHDepositManager.sol- Deposit escrowsrc/core/TDHAdditionalCollateral.sol- Additional collateral escrowsrc/core/TDHTimelock.sol- Admin timelock
We welcome security reviews from the community.