Halborn Logo

icon

Ethereum - NFTfi


Prepared by:

Halborn Logo

HALBORN

Last Updated 04/26/2024

Date of Engagement by: March 7th, 2022 - March 25th, 2022

Summary

100% of all REPORTED Findings have been addressed

All findings

1

Critical

0

High

0

Medium

0

Low

0

Informational

1


1. INTRODUCTION

\client engaged Halborn to conduct a security audit on their smart contracts beginning on 2022-03-07 and ending on 2022-03-25. The security assessment was scoped to the smart contracts provided to the Halborn team.

2. AUDIT SUMMARY

The team at Halborn was provided two weeks for the engagement and assigned a full-time security engineer to audit the security of the smart contract. The security engineer is a blockchain and smart-contract security expert with advanced penetration testing, smart-contract hacking, and deep knowledge of multiple blockchain protocols.

The purpose of this audit is to:

    • Ensure that smart contract functions operate as intended

    • Identify potential security issues with the smart contracts

In summary, Halborn identified a security risk that was acknowledged by the \client team.

3. TEST APPROACH & METHODOLOGY

Halborn performed a combination of manual and automated security testing to balance efficiency, timeliness, practicality, and accuracy in regard to the scope of this audit. While manual testing is recommended to uncover flaws in logic, process, and implementation; automated testing techniques help enhance coverage of the NFTfi develop-v2.1-audit branch and Pool V5 contract solidity code and can quickly identify items that do not follow security best practices. The following phases and associated tools were used throughout the term of the audit:

    • Research into architecture and purpose.

    • Smart contract manual code review and walkthrough.

    • Graphing out functionality and contract logic/connectivity/functions (solgraph)

    • Manual assessment of use and safety for the critical Solidity variables and functions in scope to identify any arithmetic related vulnerability classes.

    • Manual testing by custom scripts.

    • Scanning of solidity files for vulnerabilities, security hotspots or bugs. (MythX)

    • Static Analysis of security for scoped contract, and imported functions. (Slither)

    • Testnet deployment (Remix IDE)

4. SCOPE

IN-SCOPE : NFTfi v2.2-07-03-2022-audit branch contracts

The security assessment was scoped to the following smart contract: Scope-Commit-Id : 77d8476ff40e048dcca108265e863a3ebdfb261a Remediation Commit-Id : dedc4aca9d79a4eb63c507e37916bd70c5d0e9f5

    • contracts/NftfiHub.sol

    • contracts/airdrop/AirdropFlashLoan.sol

    • contracts/airdrop/AirdropReceiver.sol

    • contracts/airdrop/AirdropReceiverFactory.sol

    • contracts/airdrop/IAirdropReceiverFactory.sol

    • contracts/composable/ERC9981155Extension.sol

    • contracts/composable/ERC998ERC20Extension.sol

    • contracts/composable/ERC998TopDown.sol

    • contracts/composable/NftfiBundler.sol

    • contracts/interfaces/*.sol

    • contracts/loans/BaseLoan.sol

    • contracts/loans/direct/DirectLoanCoordinator.sol

    • contracts/loans/direct/loanTypes/DirectLoanBaseMinimal.sol

    • contracts/loans/direct/loanTypes/DirectLoanFixedOffer.sol

    • contracts/loans/direct/loanTypes/IDirectLoanBase.sol

    • contracts/loans/direct/loanTypes/LoanAirdropUtils.sol

    • contracts/loans/direct/loanTypes/LoanChecksAndCalculations.sol

    • contracts/loans/direct/loanTypes/LoanData.sol

    • contracts/nftTypeRegistry/nftTypes/CryptoKittiesWrapper.sol

    • contracts/nftTypeRegistry/nftTypes/ERC1155Wrapper.sol

    • contracts/nftTypeRegistry/nftTypes/ERC721Wrapper.sol

    • contracts/permittedLists/PermittedAirdrops.sol

    • contracts/permittedLists/PermittedERC20s.sol

    • contracts/permittedLists/PermittedNFTsAndTypeRegistry.sol

    • contracts/permittedLists/PermittedPartners.sol

    • contracts/smartNft/SmartNft.sol

    • contracts/utils/ContractKeys.sol

    • contracts/utils/NFTfiSigningUtils.sol

    • contracts/utils/NFTfiSigningUtilsContract.sol

    • contracts/utils/NftReceiver.sol

    • contracts/utils/Ownable.sol

    • contracts/utils/TokenTrade.sol

Additions 31st March 2022:

    • contracts/loans/direct/loanTypes/DirectLoanBaseBundle.sol

    • contracts/loans/direct/loanTypes/DirectLoanFixedListing.sol

    • contracts/loans/direct/loanTypes/DirectLoanProRatedOffer.sol

    • contracts/loans/direct/loanTypes/DirectLoanProRatedListing.sol

    • contracts/loans/direct/loanTypes/DirectLoanFixedBundleOffer.sol

    • contracts/loans/direct/loanTypes/DirectLoanFixedBundleListing.sol

    • contracts/loans/direct/loanTypes/DirectLoanProRatedBundleListing.sol

    • contracts/loans/direct/loanTypes/DirectLoanProRatedBundleOffer.sol

5. RISK METHODOLOGY

Vulnerabilities or issues observed by Halborn are ranked based on the risk assessment methodology by measuring the LIKELIHOOD of a security incident and the IMPACT should an incident occur. This framework works for communicating the characteristics and impacts of technology vulnerabilities. The quantitative model ensures repeatable and accurate measurement while enabling users to see the underlying vulnerability characteristics that were used to generate the Risk scores. For every vulnerability, a risk level will be calculated on a scale of 5 to 1 with 5 being the highest likelihood or impact.
RISK SCALE - LIKELIHOOD
  • 5 - Almost certain an incident will occur.
  • 4 - High probability of an incident occurring.
  • 3 - Potential of a security incident in the long term.
  • 2 - Low probability of an incident occurring.
  • 1 - Very unlikely issue will cause an incident.
RISK SCALE - IMPACT
  • 5 - May cause devastating and unrecoverable impact or loss.
  • 4 - May cause a significant level of impact or loss.
  • 3 - May cause a partial impact or loss to many.
  • 2 - May cause temporary impact or loss.
  • 1 - May cause minimal or un-noticeable impact.
The risk level is then calculated using a sum of these two values, creating a value of 10 to 1 with 10 being the highest level of security risk.
Critical
High
Medium
Low
Informational
  • 10 - CRITICAL
  • 9 - 8 - HIGH
  • 7 - 6 - MEDIUM
  • 5 - 4 - LOW
  • 3 - 1 - VERY LOW AND INFORMATIONAL

6. SCOPE

Out-of-Scope: New features/implementations after the remediation commit IDs.

7. Assessment Summary & Findings Overview

Critical

0

High

0

Medium

0

Low

0

Informational

1

Impact x Likelihood

HAL-01

Security analysisRisk levelRemediation Date
CAUSE-EFFECT CHECKSInformationalAcknowledged

8. Findings & Tech Details

8.1 CAUSE-EFFECT CHECKS

// Informational

Description

The _payBackLoan under DirectLoanBaseMinimal does not pre-check the user's balance by being payoffAmount + adminFee + revenueShare it does so incrementally.

Score
Impact: 1
Likelihood: 1
Recommendation

ACKNOWLEDGED: The NFTfi team acknowledged this issue.

9. Review Notes

AirdropFlashLoan

airdrop/AirdropFlashLoan.sol

digraph G {
  graph [ ratio = "auto", page = "100", compound =true, bgcolor = "#2e3e56" ];
  node [ style = "filled", fillcolor = "#edad56", color = "#edad56", penwidth =3 ];
  edge [ color = "#fcfcfc", penwidth =2, fontname = "helvetica Neue Ultra Light" ];
subgraph "clusterAirdropFlashLoan" {
  graph [ label = "AirdropFlashLoan", color = "#445773", fontcolor = "#f0f0f0", style = "rounded", bgcolor = "#445773" ];
  "AirdropFlashLoan.<Constructor>" [ label = "<Constructor>", color = "#FF9797", fillcolor = "#FF9797" ];
  "AirdropFlashLoan.pullAirdrop" [ label = "pullAirdrop", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "AirdropFlashLoan.supportsInterface" [ label = "supportsInterface", color = "#FF9797", fillcolor = "#FF9797" ];
  "AirdropFlashLoan._transferNFT" [ label = "_transferNFT" ];
  "AirdropFlashLoan._getSelector" [ label = "_getSelector" ];
  "AirdropFlashLoan.INftfiHub" [ label = "INftfiHub" ];
  "AirdropFlashLoan.IPermittedAirdrops" [ label = "IPermittedAirdrops" ];
  "AirdropFlashLoan.IERC1155" [ label = "IERC1155" ];
  "AirdropFlashLoan.IERC721" [ label = "IERC721" ];
  "AirdropFlashLoan.type" [ label = "type" ];
  "AirdropFlashLoan.INftWrapper" [ label = "INftWrapper" ];
}

subgraph "clusterINftfiHub" {
  graph [ label = "INftfiHub", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "INftfiHub.getContract" [ label = "getContract" ];
}

subgraph "cluster_target" {
  graph [ label = "_target", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "_target.functionCall" [ label = "functionCall" ];
}

subgraph "clusterReentrancyGuard" {
  graph [ label = "ReentrancyGuard", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "ReentrancyGuard.supportsInterface" [ label = "supportsInterface" ];
}

subgraph "cluster_nftWrapper" {
  graph [ label = "_nftWrapper", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "_nftWrapper.functionDelegateCall" [ label = "functionDelegateCall" ];
}

  "AirdropFlashLoan.<Constructor>";
  "AirdropFlashLoan.INftfiHub";
  "AirdropFlashLoan.pullAirdrop";
  "AirdropFlashLoan.IPermittedAirdrops";
  "INftfiHub.getContract";
  "AirdropFlashLoan._getSelector";
  "_target.functionCall";
  "AirdropFlashLoan._transferNFT";
  "AirdropFlashLoan.IERC1155";
  "AirdropFlashLoan.IERC721";
  "AirdropFlashLoan.supportsInterface";
  "AirdropFlashLoan.type";
  "ReentrancyGuard.supportsInterface";
  "_nftWrapper.functionDelegateCall";
  "AirdropFlashLoan.INftWrapper";
  "AirdropFlashLoan.<Constructor>" -> "AirdropFlashLoan.INftfiHub" [ color = "#1bc6a6" ];
  "AirdropFlashLoan.pullAirdrop" -> "AirdropFlashLoan.IPermittedAirdrops" [ color = "#1bc6a6" ];
  "AirdropFlashLoan.pullAirdrop" -> "INftfiHub.getContract" [ color = "white" ];
  "AirdropFlashLoan.pullAirdrop" -> "AirdropFlashLoan._getSelector" [ color = "#1bc6a6" ];
  "AirdropFlashLoan.pullAirdrop" -> "_target.functionCall" [ color = "white" ];
  "AirdropFlashLoan.pullAirdrop" -> "AirdropFlashLoan._transferNFT" [ color = "#1bc6a6" ];
  "AirdropFlashLoan.pullAirdrop" -> "AirdropFlashLoan.IERC1155" [ color = "#1bc6a6" ];
  "AirdropFlashLoan.pullAirdrop" -> "AirdropFlashLoan.IERC721" [ color = "#1bc6a6" ];
  "AirdropFlashLoan.supportsInterface" -> "AirdropFlashLoan.type" [ color = "#1bc6a6" ];
  "AirdropFlashLoan.supportsInterface" -> "ReentrancyGuard.supportsInterface" [ color = "white" ];
  "AirdropFlashLoan._transferNFT" -> "_nftWrapper.functionDelegateCall" [ color = "white" ];
  "AirdropFlashLoan._transferNFT" -> "AirdropFlashLoan.INftWrapper" [ color = "#1bc6a6" ];


rankdir=LR
node [shape=plaintext]
subgraph cluster_01 { 
label = "Legend";
key [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
  <tr><td align="right" port="i1">Internal Call</td></tr>
  <tr><td align="right" port="i2">External Call</td></tr>
  <tr><td align="right" port="i3">Defined Contract</td></tr>
  <tr><td align="right" port="i4">Undefined Contract</td></tr>
  </table>>]
key2 [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
  <tr><td port="i1">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i2">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i3" bgcolor="#445773">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i4">
    <table border="1" cellborder="0" cellspacing="0" cellpadding="7" color="#e8726d">
      <tr>
       <td></td>
      </tr>
     </table>
  </td></tr>
  </table>>]
key:i1:e -> key2:i1:w [color="#1bc6a6"]
key:i2:e -> key2:i2:w [color="white"]
}
}

AirdropReceiver

airdrop/AirdropReceiver.sol

digraph G {
  graph [ ratio = "auto", page = "100", compound =true, bgcolor = "#2e3e56" ];
  node [ style = "filled", fillcolor = "#edad56", color = "#edad56", penwidth =3 ];
  edge [ color = "#fcfcfc", penwidth =2, fontname = "helvetica Neue Ultra Light" ];
subgraph "clusterAirdropReceiver" {
  graph [ label = "AirdropReceiver", color = "#445773", fontcolor = "#f0f0f0", style = "rounded", bgcolor = "#445773" ];
  "AirdropReceiver.onlyOwner" [ label = "onlyOwner", color = "#1bc6a6", shape =doubleoctagon ];
  "AirdropReceiver.onlyOwnerOrBeneficiary" [ label = "onlyOwnerOrBeneficiary", color = "#1bc6a6", shape =doubleoctagon ];
  "AirdropReceiver.<Constructor>" [ label = "<Constructor>", color = "#FF9797", fillcolor = "#FF9797" ];
  "AirdropReceiver.getTokenId" [ label = "getTokenId" ];
  "AirdropReceiver.initialize" [ label = "initialize", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "AirdropReceiver.wrap" [ label = "wrap", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "AirdropReceiver.unwrap" [ label = "unwrap", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "AirdropReceiver.pullAirdrop" [ label = "pullAirdrop", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "AirdropReceiver.supportsInterface" [ label = "supportsInterface", color = "#FF9797", fillcolor = "#FF9797" ];
  "AirdropReceiver.drainERC20Airdrop" [ label = "drainERC20Airdrop", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "AirdropReceiver.drainERC721Airdrop" [ label = "drainERC721Airdrop", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "AirdropReceiver.drainERC1155Airdrop" [ label = "drainERC1155Airdrop", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "AirdropReceiver._transferNFT" [ label = "_transferNFT" ];
  "AirdropReceiver._getSelector" [ label = "_getSelector" ];
  "AirdropReceiver.onERC721Received" [ label = "onERC721Received", color = "#FF9797", fillcolor = "#FF9797" ];
  "AirdropReceiver.onERC1155Received" [ label = "onERC1155Received", color = "#FF9797", fillcolor = "#FF9797" ];
  "AirdropReceiver.onERC1155BatchReceived" [ label = "onERC1155BatchReceived", color = "#FF9797", fillcolor = "#FF9797" ];
  "AirdropReceiver._receiveAndWrap" [ label = "_receiveAndWrap" ];
  "AirdropReceiver.ownerOf" [ label = "ownerOf" ];
  "AirdropReceiver.INftfiHub" [ label = "INftfiHub" ];
  "AirdropReceiver._safeMint" [ label = "_safeMint" ];
  "AirdropReceiver.IPermittedNFTs" [ label = "IPermittedNFTs" ];
  "AirdropReceiver.IPermittedAirdrops" [ label = "IPermittedAirdrops" ];
  "AirdropReceiver.type" [ label = "type" ];
  "AirdropReceiver.IERC20" [ label = "IERC20" ];
  "AirdropReceiver.IERC721" [ label = "IERC721" ];
  "AirdropReceiver.IERC1155" [ label = "IERC1155" ];
  "AirdropReceiver.INftWrapper" [ label = "INftWrapper" ];
}

subgraph "clusterINftfiHub" {
  graph [ label = "INftfiHub", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "INftfiHub.getContract" [ label = "getContract" ];
}

subgraph "cluster_target" {
  graph [ label = "_target", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "_target.functionCall" [ label = "functionCall" ];
}

subgraph "clusterReentrancyGuard" {
  graph [ label = "ReentrancyGuard", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "ReentrancyGuard.supportsInterface" [ label = "supportsInterface" ];
}

subgraph "clusterIERC20" {
  graph [ label = "IERC20", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "IERC20.balanceOf" [ label = "balanceOf" ];
  "IERC20.safeTransfer" [ label = "safeTransfer" ];
}

subgraph "clusterIERC721" {
  graph [ label = "IERC721", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "IERC721.safeTransferFrom" [ label = "safeTransferFrom" ];
}

subgraph "clusterIERC1155" {
  graph [ label = "IERC1155", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "IERC1155.balanceOf" [ label = "balanceOf" ];
  "IERC1155.safeTransferFrom" [ label = "safeTransferFrom" ];
}

subgraph "cluster_nftTransferWrapper" {
  graph [ label = "_nftTransferWrapper", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "_nftTransferWrapper.functionDelegateCall" [ label = "functionDelegateCall" ];
}

  "AirdropReceiver.onlyOwner";
  "AirdropReceiver.ownerOf";
  "AirdropReceiver.getTokenId";
  "AirdropReceiver.onlyOwnerOrBeneficiary";
  "AirdropReceiver.<Constructor>";
  "AirdropReceiver.INftfiHub";
  "AirdropReceiver._safeMint";
  "AirdropReceiver.initialize";
  "AirdropReceiver.wrap";
  "AirdropReceiver.IPermittedNFTs";
  "INftfiHub.getContract";
  "AirdropReceiver._transferNFT";
  "AirdropReceiver.unwrap";
  "AirdropReceiver.pullAirdrop";
  "AirdropReceiver.IPermittedAirdrops";
  "AirdropReceiver._getSelector";
  "_target.functionCall";
  "AirdropReceiver.supportsInterface";
  "AirdropReceiver.type";
  "ReentrancyGuard.supportsInterface";
  "AirdropReceiver.drainERC20Airdrop";
  "AirdropReceiver.IERC20";
  "IERC20.balanceOf";
  "IERC20.safeTransfer";
  "AirdropReceiver.drainERC721Airdrop";
  "AirdropReceiver.IERC721";
  "IERC721.safeTransferFrom";
  "AirdropReceiver.drainERC1155Airdrop";
  "AirdropReceiver.IERC1155";
  "IERC1155.balanceOf";
  "IERC1155.safeTransferFrom";
  "_nftTransferWrapper.functionDelegateCall";
  "AirdropReceiver.INftWrapper";
  "AirdropReceiver.onERC721Received";
  "AirdropReceiver._receiveAndWrap";
  "AirdropReceiver.onERC1155Received";
  "AirdropReceiver.onERC1155BatchReceived";
  "AirdropReceiver.onlyOwner" -> "AirdropReceiver.ownerOf" [ color = "#1bc6a6" ];
  "AirdropReceiver.onlyOwner" -> "AirdropReceiver.getTokenId" [ color = "#1bc6a6" ];
  "AirdropReceiver.onlyOwnerOrBeneficiary" -> "AirdropReceiver.ownerOf" [ color = "#1bc6a6" ];
  "AirdropReceiver.onlyOwnerOrBeneficiary" -> "AirdropReceiver.getTokenId" [ color = "#1bc6a6" ];
  "AirdropReceiver.<Constructor>" -> "AirdropReceiver.INftfiHub" [ color = "#1bc6a6" ];
  "AirdropReceiver.<Constructor>" -> "AirdropReceiver._safeMint" [ color = "#1bc6a6" ];
  "AirdropReceiver.<Constructor>" -> "AirdropReceiver.getTokenId" [ color = "#1bc6a6" ];
  "AirdropReceiver.initialize" -> "AirdropReceiver.getTokenId" [ color = "#1bc6a6" ];
  "AirdropReceiver.initialize" -> "AirdropReceiver._safeMint" [ color = "#1bc6a6" ];
  "AirdropReceiver.wrap" -> "AirdropReceiver.IPermittedNFTs" [ color = "#1bc6a6" ];
  "AirdropReceiver.wrap" -> "INftfiHub.getContract" [ color = "white" ];
  "AirdropReceiver.wrap" -> "AirdropReceiver._transferNFT" [ color = "#1bc6a6" ];
  "AirdropReceiver.unwrap" -> "AirdropReceiver._transferNFT" [ color = "#1bc6a6" ];
  "AirdropReceiver.pullAirdrop" -> "AirdropReceiver.IPermittedAirdrops" [ color = "#1bc6a6" ];
  "AirdropReceiver.pullAirdrop" -> "INftfiHub.getContract" [ color = "white" ];
  "AirdropReceiver.pullAirdrop" -> "AirdropReceiver._getSelector" [ color = "#1bc6a6" ];
  "AirdropReceiver.pullAirdrop" -> "_target.functionCall" [ color = "white" ];
  "AirdropReceiver.supportsInterface" -> "AirdropReceiver.type" [ color = "#1bc6a6" ];
  "AirdropReceiver.supportsInterface" -> "ReentrancyGuard.supportsInterface" [ color = "white" ];
  "AirdropReceiver.drainERC20Airdrop" -> "AirdropReceiver.IERC20" [ color = "#1bc6a6" ];
  "AirdropReceiver.drainERC20Airdrop" -> "IERC20.balanceOf" [ color = "white" ];
  "AirdropReceiver.drainERC20Airdrop" -> "IERC20.safeTransfer" [ color = "white" ];
  "AirdropReceiver.drainERC721Airdrop" -> "AirdropReceiver.IERC721" [ color = "#1bc6a6" ];
  "AirdropReceiver.drainERC721Airdrop" -> "IERC721.safeTransferFrom" [ color = "white" ];
  "AirdropReceiver.drainERC1155Airdrop" -> "AirdropReceiver.IERC1155" [ color = "#1bc6a6" ];
  "AirdropReceiver.drainERC1155Airdrop" -> "IERC1155.balanceOf" [ color = "white" ];
  "AirdropReceiver.drainERC1155Airdrop" -> "IERC1155.safeTransferFrom" [ color = "white" ];
  "AirdropReceiver._transferNFT" -> "_nftTransferWrapper.functionDelegateCall" [ color = "white" ];
  "AirdropReceiver._transferNFT" -> "AirdropReceiver.INftWrapper" [ color = "#1bc6a6" ];
  "AirdropReceiver.onERC721Received" -> "AirdropReceiver.ownerOf" [ color = "#1bc6a6" ];
  "AirdropReceiver.onERC721Received" -> "AirdropReceiver.getTokenId" [ color = "#1bc6a6" ];
  "AirdropReceiver.onERC721Received" -> "AirdropReceiver._receiveAndWrap" [ color = "#1bc6a6" ];
  "AirdropReceiver.onERC1155Received" -> "AirdropReceiver.ownerOf" [ color = "#1bc6a6" ];
  "AirdropReceiver.onERC1155Received" -> "AirdropReceiver.getTokenId" [ color = "#1bc6a6" ];
  "AirdropReceiver.onERC1155Received" -> "AirdropReceiver._receiveAndWrap" [ color = "#1bc6a6" ];
  "AirdropReceiver.onERC1155BatchReceived" -> "AirdropReceiver.ownerOf" [ color = "#1bc6a6" ];
  "AirdropReceiver.onERC1155BatchReceived" -> "AirdropReceiver.getTokenId" [ color = "#1bc6a6" ];
  "AirdropReceiver.onERC1155BatchReceived" -> "AirdropReceiver._receiveAndWrap" [ color = "#1bc6a6" ];
  "AirdropReceiver._receiveAndWrap" -> "AirdropReceiver.IPermittedNFTs" [ color = "#1bc6a6" ];
  "AirdropReceiver._receiveAndWrap" -> "INftfiHub.getContract" [ color = "white" ];


rankdir=LR
node [shape=plaintext]
subgraph cluster_01 { 
label = "Legend";
key [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
  <tr><td align="right" port="i1">Internal Call</td></tr>
  <tr><td align="right" port="i2">External Call</td></tr>
  <tr><td align="right" port="i3">Defined Contract</td></tr>
  <tr><td align="right" port="i4">Undefined Contract</td></tr>
  </table>>]
key2 [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
  <tr><td port="i1">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i2">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i3" bgcolor="#445773">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i4">
    <table border="1" cellborder="0" cellspacing="0" cellpadding="7" color="#e8726d">
      <tr>
       <td></td>
      </tr>
     </table>
  </td></tr>
  </table>>]
key:i1:e -> key2:i1:w [color="#1bc6a6"]
key:i2:e -> key2:i2:w [color="white"]
}
}

DirectLoanBaseMinimal

loans/direct/loanTypes/DirectLoanBaseMinimal.sol

digraph G {
  graph [ ratio = "auto", page = "100", compound =true, bgcolor = "#2e3e56" ];
  node [ style = "filled", fillcolor = "#edad56", color = "#edad56", penwidth =3 ];
  edge [ color = "#fcfcfc", penwidth =2, fontname = "helvetica Neue Ultra Light" ];
subgraph "clusterDirectLoanBaseMinimal" {
  graph [ label = "DirectLoanBaseMinimal", color = "#445773", fontcolor = "#f0f0f0", style = "rounded", bgcolor = "#445773" ];
  "DirectLoanBaseMinimal.<Constructor>" [ label = "<Constructor>", color = "#FF9797", fillcolor = "#FF9797" ];
  "DirectLoanBaseMinimal.updateMaximumLoanDuration" [ label = "updateMaximumLoanDuration", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.updateAdminFee" [ label = "updateAdminFee", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.drainERC20Airdrop" [ label = "drainERC20Airdrop", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.setERC20Permit" [ label = "setERC20Permit", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.setERC20Permits" [ label = "setERC20Permits", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.drainERC721Airdrop" [ label = "drainERC721Airdrop", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.drainERC1155Airdrop" [ label = "drainERC1155Airdrop", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.mintObligationReceipt" [ label = "mintObligationReceipt", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.renegotiateLoan" [ label = "renegotiateLoan", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.payBackLoan" [ label = "payBackLoan", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.liquidateOverdueLoan" [ label = "liquidateOverdueLoan", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.pullAirdrop" [ label = "pullAirdrop", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.wrapCollateral" [ label = "wrapCollateral", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.cancelLoanCommitmentBeforeLoanHasBegun" [ label = "cancelLoanCommitmentBeforeLoanHasBegun", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.getPayoffAmount" [ label = "getPayoffAmount", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.getWhetherNonceHasBeenUsedForUser" [ label = "getWhetherNonceHasBeenUsedForUser", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanBaseMinimal.getERC20Permit" [ label = "getERC20Permit" ];
  "DirectLoanBaseMinimal._renegotiateLoan" [ label = "_renegotiateLoan" ];
  "DirectLoanBaseMinimal._createLoan" [ label = "_createLoan", color = "#f2c383", fillcolor = "#f2c383" ];
  "DirectLoanBaseMinimal._createLoanNoNftTransfer" [ label = "_createLoanNoNftTransfer" ];
  "DirectLoanBaseMinimal._transferNFT" [ label = "_transferNFT" ];
  "DirectLoanBaseMinimal._payBackLoan" [ label = "_payBackLoan" ];
  "DirectLoanBaseMinimal._resolveLoan" [ label = "_resolveLoan" ];
  "DirectLoanBaseMinimal._resolveLoanNoNftTransfer" [ label = "_resolveLoanNoNftTransfer" ];
  "DirectLoanBaseMinimal._setERC20Permit" [ label = "_setERC20Permit" ];
  "DirectLoanBaseMinimal._loanSanityChecks" [ label = "_loanSanityChecks", color = "#f2c383", fillcolor = "#f2c383" ];
  "DirectLoanBaseMinimal._getPartiesAndData" [ label = "_getPartiesAndData" ];
  "DirectLoanBaseMinimal._setupLoanExtras" [ label = "_setupLoanExtras", color = "#f2c383", fillcolor = "#f2c383" ];
  "DirectLoanBaseMinimal._payoffAndFee" [ label = "_payoffAndFee" ];
  "DirectLoanBaseMinimal._getWrapper" [ label = "_getWrapper", color = "#f2c383", fillcolor = "#f2c383" ];
  "DirectLoanBaseMinimal.INftfiHub" [ label = "INftfiHub" ];
  "DirectLoanBaseMinimal.type" [ label = "type" ];
  "DirectLoanBaseMinimal.IERC20" [ label = "IERC20" ];
  "DirectLoanBaseMinimal.IERC721" [ label = "IERC721" ];
  "DirectLoanBaseMinimal.IERC1155" [ label = "IERC1155" ];
  "DirectLoanBaseMinimal.IDirectLoanCoordinator" [ label = "IDirectLoanCoordinator" ];
  "DirectLoanBaseMinimal.Signature" [ label = "Signature" ];
  "DirectLoanBaseMinimal.owner" [ label = "owner" ];
  "DirectLoanBaseMinimal.INftWrapper" [ label = "INftWrapper" ];
  "DirectLoanBaseMinimal.LoanExtras" [ label = "LoanExtras" ];
  "DirectLoanBaseMinimal.IPermittedNFTs" [ label = "IPermittedNFTs" ];
}

subgraph "clusterIERC20" {
  graph [ label = "IERC20", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "IERC20.balanceOf" [ label = "balanceOf" ];
  "IERC20.safeTransfer" [ label = "safeTransfer" ];
}

subgraph "clusterIERC721" {
  graph [ label = "IERC721", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "IERC721.ownerOf" [ label = "ownerOf" ];
  "IERC721.safeTransferFrom" [ label = "safeTransferFrom" ];
}

subgraph "clusterIERC1155" {
  graph [ label = "IERC1155", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "IERC1155.balanceOf" [ label = "balanceOf" ];
  "IERC1155.safeTransferFrom" [ label = "safeTransferFrom" ];
}

subgraph "clusterINftfiHub" {
  graph [ label = "INftfiHub", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "INftfiHub.getContract" [ label = "getContract" ];
}

subgraph "clusterIDirectLoanCoordinator" {
  graph [ label = "IDirectLoanCoordinator", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "IDirectLoanCoordinator.mintObligationReceipt" [ label = "mintObligationReceipt" ];
  "IDirectLoanCoordinator.registerLoan" [ label = "registerLoan" ];
  "IDirectLoanCoordinator.resolveLoan" [ label = "resolveLoan" ];
  "IDirectLoanCoordinator.getLoanData" [ label = "getLoanData" ];
  "IDirectLoanCoordinator.obligationReceiptToken" [ label = "obligationReceiptToken" ];
  "IDirectLoanCoordinator.promissoryNoteToken" [ label = "promissoryNoteToken" ];
}

subgraph "clusterLoanChecksAndCalculations" {
  graph [ label = "LoanChecksAndCalculations", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "LoanChecksAndCalculations.payBackChecks" [ label = "payBackChecks" ];
  "LoanChecksAndCalculations.checkLoanIdValidity" [ label = "checkLoanIdValidity" ];
  "LoanChecksAndCalculations.renegotiationChecks" [ label = "renegotiationChecks" ];
  "LoanChecksAndCalculations.computeAdminFee" [ label = "computeAdminFee" ];
  "LoanChecksAndCalculations.computeReferralFee" [ label = "computeReferralFee" ];
  "LoanChecksAndCalculations.computeRevenueShare" [ label = "computeRevenueShare" ];
  "LoanChecksAndCalculations.getRevenueSharePercent" [ label = "getRevenueSharePercent" ];
}

subgraph "clusterLoanAirdropUtils" {
  graph [ label = "LoanAirdropUtils", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "LoanAirdropUtils.pullAirdrop" [ label = "pullAirdrop" ];
  "LoanAirdropUtils.wrapCollateral" [ label = "wrapCollateral" ];
}

subgraph "clusterNFTfiSigningUtils" {
  graph [ label = "NFTfiSigningUtils", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "NFTfiSigningUtils.isValidLenderRenegotiationSignature" [ label = "isValidLenderRenegotiationSignature" ];
}

subgraph "clusterAddress" {
  graph [ label = "Address", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "Address.functionDelegateCall" [ label = "functionDelegateCall" ];
}

  "DirectLoanBaseMinimal.<Constructor>";
  "DirectLoanBaseMinimal.INftfiHub";
  "DirectLoanBaseMinimal._setERC20Permit";
  "DirectLoanBaseMinimal.updateMaximumLoanDuration";
  "DirectLoanBaseMinimal.type";
  "DirectLoanBaseMinimal.drainERC20Airdrop";
  "DirectLoanBaseMinimal.IERC20";
  "IERC20.balanceOf";
  "IERC20.safeTransfer";
  "DirectLoanBaseMinimal.setERC20Permit";
  "DirectLoanBaseMinimal.setERC20Permits";
  "DirectLoanBaseMinimal.drainERC721Airdrop";
  "DirectLoanBaseMinimal.IERC721";
  "IERC721.ownerOf";
  "IERC721.safeTransferFrom";
  "DirectLoanBaseMinimal.drainERC1155Airdrop";
  "DirectLoanBaseMinimal.IERC1155";
  "IERC1155.balanceOf";
  "IERC1155.safeTransferFrom";
  "DirectLoanBaseMinimal.mintObligationReceipt";
  "DirectLoanBaseMinimal.IDirectLoanCoordinator";
  "INftfiHub.getContract";
  "IDirectLoanCoordinator.mintObligationReceipt";
  "DirectLoanBaseMinimal.renegotiateLoan";
  "DirectLoanBaseMinimal._renegotiateLoan";
  "DirectLoanBaseMinimal.payBackLoan";
  "LoanChecksAndCalculations.payBackChecks";
  "DirectLoanBaseMinimal._getPartiesAndData";
  "DirectLoanBaseMinimal._payBackLoan";
  "DirectLoanBaseMinimal._resolveLoan";
  "DirectLoanBaseMinimal.liquidateOverdueLoan";
  "LoanChecksAndCalculations.checkLoanIdValidity";
  "DirectLoanBaseMinimal.pullAirdrop";
  "LoanAirdropUtils.pullAirdrop";
  "DirectLoanBaseMinimal.wrapCollateral";
  "LoanAirdropUtils.wrapCollateral";
  "LoanChecksAndCalculations.renegotiationChecks";
  "NFTfiSigningUtils.isValidLenderRenegotiationSignature";
  "DirectLoanBaseMinimal.Signature";
  "LoanChecksAndCalculations.computeAdminFee";
  "DirectLoanBaseMinimal.owner";
  "DirectLoanBaseMinimal._createLoan";
  "DirectLoanBaseMinimal._transferNFT";
  "DirectLoanBaseMinimal._createLoanNoNftTransfer";
  "LoanChecksAndCalculations.computeReferralFee";
  "IDirectLoanCoordinator.registerLoan";
  "Address.functionDelegateCall";
  "DirectLoanBaseMinimal.INftWrapper";
  "DirectLoanBaseMinimal._payoffAndFee";
  "LoanChecksAndCalculations.computeRevenueShare";
  "DirectLoanBaseMinimal._resolveLoanNoNftTransfer";
  "IDirectLoanCoordinator.resolveLoan";
  "DirectLoanBaseMinimal._loanSanityChecks";
  "DirectLoanBaseMinimal.getERC20Permit";
  "IDirectLoanCoordinator.getLoanData";
  "IDirectLoanCoordinator.obligationReceiptToken";
  "IDirectLoanCoordinator.promissoryNoteToken";
  "DirectLoanBaseMinimal._setupLoanExtras";
  "DirectLoanBaseMinimal.LoanExtras";
  "LoanChecksAndCalculations.getRevenueSharePercent";
  "DirectLoanBaseMinimal._getWrapper";
  "DirectLoanBaseMinimal.IPermittedNFTs";
  "DirectLoanBaseMinimal.<Constructor>" -> "DirectLoanBaseMinimal.INftfiHub" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.<Constructor>" -> "DirectLoanBaseMinimal._setERC20Permit" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.updateMaximumLoanDuration" -> "DirectLoanBaseMinimal.type" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.drainERC20Airdrop" -> "DirectLoanBaseMinimal.IERC20" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.drainERC20Airdrop" -> "IERC20.balanceOf" [ color = "white" ];
  "DirectLoanBaseMinimal.drainERC20Airdrop" -> "IERC20.safeTransfer" [ color = "white" ];
  "DirectLoanBaseMinimal.setERC20Permit" -> "DirectLoanBaseMinimal._setERC20Permit" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.setERC20Permits" -> "DirectLoanBaseMinimal._setERC20Permit" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.drainERC721Airdrop" -> "DirectLoanBaseMinimal.IERC721" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.drainERC721Airdrop" -> "IERC721.ownerOf" [ color = "white" ];
  "DirectLoanBaseMinimal.drainERC721Airdrop" -> "IERC721.safeTransferFrom" [ color = "white" ];
  "DirectLoanBaseMinimal.drainERC1155Airdrop" -> "DirectLoanBaseMinimal.IERC1155" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.drainERC1155Airdrop" -> "IERC1155.balanceOf" [ color = "white" ];
  "DirectLoanBaseMinimal.drainERC1155Airdrop" -> "IERC1155.safeTransferFrom" [ color = "white" ];
  "DirectLoanBaseMinimal.mintObligationReceipt" -> "DirectLoanBaseMinimal.IDirectLoanCoordinator" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.mintObligationReceipt" -> "INftfiHub.getContract" [ color = "white" ];
  "DirectLoanBaseMinimal.mintObligationReceipt" -> "IDirectLoanCoordinator.mintObligationReceipt" [ color = "white" ];
  "DirectLoanBaseMinimal.renegotiateLoan" -> "DirectLoanBaseMinimal._renegotiateLoan" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.payBackLoan" -> "LoanChecksAndCalculations.payBackChecks" [ color = "white" ];
  "DirectLoanBaseMinimal.payBackLoan" -> "DirectLoanBaseMinimal._getPartiesAndData" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.payBackLoan" -> "DirectLoanBaseMinimal._payBackLoan" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.payBackLoan" -> "DirectLoanBaseMinimal._resolveLoan" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.liquidateOverdueLoan" -> "LoanChecksAndCalculations.checkLoanIdValidity" [ color = "white" ];
  "DirectLoanBaseMinimal.liquidateOverdueLoan" -> "DirectLoanBaseMinimal._getPartiesAndData" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.liquidateOverdueLoan" -> "DirectLoanBaseMinimal._resolveLoan" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal.pullAirdrop" -> "LoanChecksAndCalculations.checkLoanIdValidity" [ color = "white" ];
  "DirectLoanBaseMinimal.pullAirdrop" -> "LoanAirdropUtils.pullAirdrop" [ color = "white" ];
  "DirectLoanBaseMinimal.wrapCollateral" -> "LoanChecksAndCalculations.checkLoanIdValidity" [ color = "white" ];
  "DirectLoanBaseMinimal.wrapCollateral" -> "LoanAirdropUtils.wrapCollateral" [ color = "white" ];
  "DirectLoanBaseMinimal._renegotiateLoan" -> "LoanChecksAndCalculations.renegotiationChecks" [ color = "white" ];
  "DirectLoanBaseMinimal._renegotiateLoan" -> "NFTfiSigningUtils.isValidLenderRenegotiationSignature" [ color = "white" ];
  "DirectLoanBaseMinimal._renegotiateLoan" -> "DirectLoanBaseMinimal.Signature" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._renegotiateLoan" -> "LoanChecksAndCalculations.computeAdminFee" [ color = "white" ];
  "DirectLoanBaseMinimal._renegotiateLoan" -> "DirectLoanBaseMinimal.IERC20" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._renegotiateLoan" -> "DirectLoanBaseMinimal.IERC20" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._renegotiateLoan" -> "DirectLoanBaseMinimal.owner" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._createLoan" -> "DirectLoanBaseMinimal._transferNFT" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._createLoan" -> "DirectLoanBaseMinimal._createLoanNoNftTransfer" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._createLoanNoNftTransfer" -> "LoanChecksAndCalculations.computeReferralFee" [ color = "white" ];
  "DirectLoanBaseMinimal._createLoanNoNftTransfer" -> "DirectLoanBaseMinimal.IERC20" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._createLoanNoNftTransfer" -> "DirectLoanBaseMinimal.IERC20" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._createLoanNoNftTransfer" -> "DirectLoanBaseMinimal.IDirectLoanCoordinator" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._createLoanNoNftTransfer" -> "INftfiHub.getContract" [ color = "white" ];
  "DirectLoanBaseMinimal._createLoanNoNftTransfer" -> "IDirectLoanCoordinator.registerLoan" [ color = "white" ];
  "DirectLoanBaseMinimal._transferNFT" -> "Address.functionDelegateCall" [ color = "white" ];
  "DirectLoanBaseMinimal._transferNFT" -> "DirectLoanBaseMinimal.INftWrapper" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._payBackLoan" -> "DirectLoanBaseMinimal._payoffAndFee" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._payBackLoan" -> "DirectLoanBaseMinimal.IERC20" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._payBackLoan" -> "LoanChecksAndCalculations.computeRevenueShare" [ color = "white" ];
  "DirectLoanBaseMinimal._payBackLoan" -> "DirectLoanBaseMinimal.IERC20" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._payBackLoan" -> "DirectLoanBaseMinimal.IERC20" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._payBackLoan" -> "DirectLoanBaseMinimal.owner" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._resolveLoan" -> "DirectLoanBaseMinimal._resolveLoanNoNftTransfer" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._resolveLoan" -> "DirectLoanBaseMinimal._transferNFT" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._resolveLoanNoNftTransfer" -> "IDirectLoanCoordinator.resolveLoan" [ color = "white" ];
  "DirectLoanBaseMinimal._loanSanityChecks" -> "DirectLoanBaseMinimal.getERC20Permit" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._getPartiesAndData" -> "DirectLoanBaseMinimal.IDirectLoanCoordinator" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._getPartiesAndData" -> "INftfiHub.getContract" [ color = "white" ];
  "DirectLoanBaseMinimal._getPartiesAndData" -> "IDirectLoanCoordinator.getLoanData" [ color = "white" ];
  "DirectLoanBaseMinimal._getPartiesAndData" -> "DirectLoanBaseMinimal.IERC721" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._getPartiesAndData" -> "IDirectLoanCoordinator.obligationReceiptToken" [ color = "white" ];
  "DirectLoanBaseMinimal._getPartiesAndData" -> "DirectLoanBaseMinimal.IERC721" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._getPartiesAndData" -> "IDirectLoanCoordinator.promissoryNoteToken" [ color = "white" ];
  "DirectLoanBaseMinimal._setupLoanExtras" -> "DirectLoanBaseMinimal.LoanExtras" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._setupLoanExtras" -> "LoanChecksAndCalculations.getRevenueSharePercent" [ color = "white" ];
  "DirectLoanBaseMinimal._getWrapper" -> "DirectLoanBaseMinimal.IPermittedNFTs" [ color = "#1bc6a6" ];
  "DirectLoanBaseMinimal._getWrapper" -> "INftfiHub.getContract" [ color = "white" ];


rankdir=LR
node [shape=plaintext]
subgraph cluster_01 { 
label = "Legend";
key [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
  <tr><td align="right" port="i1">Internal Call</td></tr>
  <tr><td align="right" port="i2">External Call</td></tr>
  <tr><td align="right" port="i3">Defined Contract</td></tr>
  <tr><td align="right" port="i4">Undefined Contract</td></tr>
  </table>>]
key2 [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
  <tr><td port="i1">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i2">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i3" bgcolor="#445773">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i4">
    <table border="1" cellborder="0" cellspacing="0" cellpadding="7" color="#e8726d">
      <tr>
       <td></td>
      </tr>
     </table>
  </td></tr>
  </table>>]
key:i1:e -> key2:i1:w [color="#1bc6a6"]
key:i2:e -> key2:i2:w [color="white"]
}
}

DirectLoanFixedOffer

loans/direct/loanTypes/DirectLoanFixedOffer.sol

digraph G {
  graph [ ratio = "auto", page = "100", compound =true, bgcolor = "#2e3e56" ];
  node [ style = "filled", fillcolor = "#edad56", color = "#edad56", penwidth =3 ];
  edge [ color = "#fcfcfc", penwidth =2, fontname = "helvetica Neue Ultra Light" ];
subgraph "clusterDirectLoanFixedOffer" {
  graph [ label = "DirectLoanFixedOffer", color = "#445773", fontcolor = "#f0f0f0", style = "rounded", bgcolor = "#445773" ];
  "DirectLoanFixedOffer.<Constructor>" [ label = "<Constructor>", color = "#FF9797", fillcolor = "#FF9797" ];
  "DirectLoanFixedOffer.acceptOffer" [ label = "acceptOffer", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanFixedOffer.getPayoffAmount" [ label = "getPayoffAmount", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanFixedOffer._acceptOffer" [ label = "_acceptOffer" ];
  "DirectLoanFixedOffer._setupLoanTerms" [ label = "_setupLoanTerms" ];
  "DirectLoanFixedOffer._payoffAndFee" [ label = "_payoffAndFee", color = "#f2c383", fillcolor = "#f2c383" ];
  "DirectLoanFixedOffer._loanSanityChecksOffer" [ label = "_loanSanityChecksOffer" ];
  "DirectLoanFixedOffer._getWrapper" [ label = "_getWrapper" ];
  "DirectLoanFixedOffer._loanSanityChecks" [ label = "_loanSanityChecks" ];
  "DirectLoanFixedOffer._setupLoanExtras" [ label = "_setupLoanExtras" ];
  "DirectLoanFixedOffer._createLoan" [ label = "_createLoan" ];
  "DirectLoanFixedOffer.LoanStarted" [ label = "LoanStarted" ];
  "DirectLoanFixedOffer.LoanTerms" [ label = "LoanTerms" ];
}

subgraph "clusterContractKeys" {
  graph [ label = "ContractKeys", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "ContractKeys.getIdFromStringKey" [ label = "getIdFromStringKey" ];
}

subgraph "clusterhub" {
  graph [ label = "hub", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "hub.getContract" [ label = "getContract" ];
}

subgraph "clusterLoanChecksAndCalculations" {
  graph [ label = "LoanChecksAndCalculations", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "LoanChecksAndCalculations.computeAdminFee" [ label = "computeAdminFee" ];
}

  "DirectLoanFixedOffer.<Constructor>";
  "ContractKeys.getIdFromStringKey";
  "DirectLoanFixedOffer.acceptOffer";
  "DirectLoanFixedOffer._getWrapper";
  "DirectLoanFixedOffer._loanSanityChecks";
  "DirectLoanFixedOffer._loanSanityChecksOffer";
  "DirectLoanFixedOffer._acceptOffer";
  "DirectLoanFixedOffer._setupLoanTerms";
  "DirectLoanFixedOffer._setupLoanExtras";
  "hub.getContract";
  "DirectLoanFixedOffer._createLoan";
  "DirectLoanFixedOffer.LoanStarted";
  "DirectLoanFixedOffer.LoanTerms";
  "DirectLoanFixedOffer._payoffAndFee";
  "LoanChecksAndCalculations.computeAdminFee";
  "DirectLoanFixedOffer.<Constructor>" -> "ContractKeys.getIdFromStringKey" [ color = "white" ];
  "DirectLoanFixedOffer.acceptOffer" -> "DirectLoanFixedOffer._getWrapper" [ color = "#1bc6a6" ];
  "DirectLoanFixedOffer.acceptOffer" -> "DirectLoanFixedOffer._loanSanityChecks" [ color = "#1bc6a6" ];
  "DirectLoanFixedOffer.acceptOffer" -> "DirectLoanFixedOffer._loanSanityChecksOffer" [ color = "#1bc6a6" ];
  "DirectLoanFixedOffer.acceptOffer" -> "DirectLoanFixedOffer._acceptOffer" [ color = "#1bc6a6" ];
  "DirectLoanFixedOffer.acceptOffer" -> "DirectLoanFixedOffer._setupLoanTerms" [ color = "#1bc6a6" ];
  "DirectLoanFixedOffer.acceptOffer" -> "DirectLoanFixedOffer._setupLoanExtras" [ color = "#1bc6a6" ];
  "DirectLoanFixedOffer._acceptOffer" -> "hub.getContract" [ color = "white" ];
  "DirectLoanFixedOffer._acceptOffer" -> "DirectLoanFixedOffer._createLoan" [ color = "#1bc6a6" ];
  "DirectLoanFixedOffer._acceptOffer" -> "DirectLoanFixedOffer.LoanStarted" [ color = "#1bc6a6" ];
  "DirectLoanFixedOffer._setupLoanTerms" -> "DirectLoanFixedOffer.LoanTerms" [ color = "#1bc6a6" ];
  "DirectLoanFixedOffer._payoffAndFee" -> "LoanChecksAndCalculations.computeAdminFee" [ color = "white" ];


rankdir=LR
node [shape=plaintext]
subgraph cluster_01 { 
label = "Legend";
key [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
  <tr><td align="right" port="i1">Internal Call</td></tr>
  <tr><td align="right" port="i2">External Call</td></tr>
  <tr><td align="right" port="i3">Defined Contract</td></tr>
  <tr><td align="right" port="i4">Undefined Contract</td></tr>
  </table>>]
key2 [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
  <tr><td port="i1">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i2">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i3" bgcolor="#445773">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i4">
    <table border="1" cellborder="0" cellspacing="0" cellpadding="7" color="#e8726d">
      <tr>
       <td></td>
      </tr>
     </table>
  </td></tr>
  </table>>]
key:i1:e -> key2:i1:w [color="#1bc6a6"]
key:i2:e -> key2:i2:w [color="white"]
}
}

DirectLoanCoordinator

loans/direct/DirectLoanCoordinator.sol

digraph G {
  graph [ ratio = "auto", page = "100", compound =true, bgcolor = "#2e3e56" ];
  node [ style = "filled", fillcolor = "#edad56", color = "#edad56", penwidth =3 ];
  edge [ color = "#fcfcfc", penwidth =2, fontname = "helvetica Neue Ultra Light" ];
subgraph "clusterDirectLoanCoordinator" {
  graph [ label = "DirectLoanCoordinator", color = "#445773", fontcolor = "#f0f0f0", style = "rounded", bgcolor = "#445773" ];
  "DirectLoanCoordinator.onlyInitialized" [ label = "onlyInitialized", color = "#1bc6a6", shape =doubleoctagon ];
  "DirectLoanCoordinator.<Constructor>" [ label = "<Constructor>", color = "#FF9797", fillcolor = "#FF9797" ];
  "DirectLoanCoordinator.initialize" [ label = "initialize", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanCoordinator.registerLoan" [ label = "registerLoan", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanCoordinator.mintObligationReceipt" [ label = "mintObligationReceipt", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanCoordinator.resolveLoan" [ label = "resolveLoan", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanCoordinator.getLoanData" [ label = "getLoanData", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanCoordinator.isValidLoanId" [ label = "isValidLoanId", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanCoordinator.registerLoanType" [ label = "registerLoanType", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanCoordinator.registerLoanTypes" [ label = "registerLoanTypes", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "DirectLoanCoordinator.getContractFromType" [ label = "getContractFromType" ];
  "DirectLoanCoordinator.getTypeFromContract" [ label = "getTypeFromContract" ];
  "DirectLoanCoordinator._registerLoanType" [ label = "_registerLoanType" ];
  "DirectLoanCoordinator._registerLoanTypes" [ label = "_registerLoanTypes" ];
  "DirectLoanCoordinator.INftfiHub" [ label = "INftfiHub" ];
  "DirectLoanCoordinator.Loan" [ label = "Loan" ];
  "DirectLoanCoordinator.SmartNft" [ label = "SmartNft" ];
}

subgraph "clusterContractKeys" {
  graph [ label = "ContractKeys", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "ContractKeys.getIdFromStringKey" [ label = "getIdFromStringKey" ];
}

  "DirectLoanCoordinator.<Constructor>";
  "DirectLoanCoordinator.INftfiHub";
  "DirectLoanCoordinator._registerLoanTypes";
  "DirectLoanCoordinator.registerLoan";
  "DirectLoanCoordinator.getContractFromType";
  "DirectLoanCoordinator.Loan";
  "DirectLoanCoordinator.SmartNft";
  "DirectLoanCoordinator.mintObligationReceipt";
  "DirectLoanCoordinator.getTypeFromContract";
  "DirectLoanCoordinator.resolveLoan";
  "DirectLoanCoordinator.registerLoanType";
  "DirectLoanCoordinator._registerLoanType";
  "DirectLoanCoordinator.registerLoanTypes";
  "ContractKeys.getIdFromStringKey";
  "DirectLoanCoordinator.<Constructor>" -> "DirectLoanCoordinator.INftfiHub" [ color = "#1bc6a6" ];
  "DirectLoanCoordinator.<Constructor>" -> "DirectLoanCoordinator._registerLoanTypes" [ color = "#1bc6a6" ];
  "DirectLoanCoordinator.registerLoan" -> "DirectLoanCoordinator.getContractFromType" [ color = "#1bc6a6" ];
  "DirectLoanCoordinator.registerLoan" -> "DirectLoanCoordinator.Loan" [ color = "#1bc6a6" ];
  "DirectLoanCoordinator.registerLoan" -> "DirectLoanCoordinator.SmartNft" [ color = "#1bc6a6" ];
  "DirectLoanCoordinator.mintObligationReceipt" -> "DirectLoanCoordinator.getTypeFromContract" [ color = "#1bc6a6" ];
  "DirectLoanCoordinator.mintObligationReceipt" -> "DirectLoanCoordinator.SmartNft" [ color = "#1bc6a6" ];
  "DirectLoanCoordinator.mintObligationReceipt" -> "DirectLoanCoordinator.SmartNft" [ color = "#1bc6a6" ];
  "DirectLoanCoordinator.mintObligationReceipt" -> "DirectLoanCoordinator.SmartNft" [ color = "#1bc6a6" ];
  "DirectLoanCoordinator.resolveLoan" -> "DirectLoanCoordinator.SmartNft" [ color = "#1bc6a6" ];
  "DirectLoanCoordinator.resolveLoan" -> "DirectLoanCoordinator.SmartNft" [ color = "#1bc6a6" ];
  "DirectLoanCoordinator.resolveLoan" -> "DirectLoanCoordinator.SmartNft" [ color = "#1bc6a6" ];
  "DirectLoanCoordinator.registerLoanType" -> "DirectLoanCoordinator._registerLoanType" [ color = "#1bc6a6" ];
  "DirectLoanCoordinator.registerLoanTypes" -> "DirectLoanCoordinator._registerLoanTypes" [ color = "#1bc6a6" ];
  "DirectLoanCoordinator._registerLoanType" -> "ContractKeys.getIdFromStringKey" [ color = "white" ];
  "DirectLoanCoordinator._registerLoanTypes" -> "DirectLoanCoordinator._registerLoanType" [ color = "#1bc6a6" ];


rankdir=LR
node [shape=plaintext]
subgraph cluster_01 { 
label = "Legend";
key [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
  <tr><td align="right" port="i1">Internal Call</td></tr>
  <tr><td align="right" port="i2">External Call</td></tr>
  <tr><td align="right" port="i3">Defined Contract</td></tr>
  <tr><td align="right" port="i4">Undefined Contract</td></tr>
  </table>>]
key2 [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
  <tr><td port="i1">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i2">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i3" bgcolor="#445773">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i4">
    <table border="1" cellborder="0" cellspacing="0" cellpadding="7" color="#e8726d">
      <tr>
       <td></td>
      </tr>
     </table>
  </td></tr>
  </table>>]
key:i1:e -> key2:i1:w [color="#1bc6a6"]
key:i2:e -> key2:i2:w [color="white"]
}
}

SmartNFT

smartNFT/SmartNFT.sol

digraph G {
  graph [ ratio = "auto", page = "100", compound =true, bgcolor = "#2e3e56" ];
  node [ style = "filled", fillcolor = "#edad56", color = "#edad56", penwidth =3 ];
  edge [ color = "#fcfcfc", penwidth =2, fontname = "helvetica Neue Ultra Light" ];
subgraph "clusterSmartNft" {
  graph [ label = "SmartNft", color = "#445773", fontcolor = "#f0f0f0", style = "rounded", bgcolor = "#445773" ];
  "SmartNft.<Constructor>" [ label = "<Constructor>", color = "#FF9797", fillcolor = "#FF9797" ];
  "SmartNft.setLoanCoordinator" [ label = "setLoanCoordinator", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "SmartNft.mint" [ label = "mint", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "SmartNft.burn" [ label = "burn", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "SmartNft.setBaseURI" [ label = "setBaseURI", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "SmartNft.exists" [ label = "exists", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "SmartNft.supportsInterface" [ label = "supportsInterface", color = "#FF9797", fillcolor = "#FF9797" ];
  "SmartNft._setBaseURI" [ label = "_setBaseURI" ];
  "SmartNft._baseURI" [ label = "_baseURI", color = "#f2c383", fillcolor = "#f2c383" ];
  "SmartNft._getChainID" [ label = "_getChainID" ];
  "SmartNft._setupRole" [ label = "_setupRole" ];
  "SmartNft.INftfiHub" [ label = "INftfiHub" ];
  "SmartNft.grantRole" [ label = "grantRole" ];
  "SmartNft._safeMint" [ label = "_safeMint" ];
  "SmartNft._burn" [ label = "_burn" ];
  "SmartNft._exists" [ label = "_exists" ];
}

subgraph "clusterAccessControl" {
  graph [ label = "AccessControl", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "AccessControl.supportsInterface" [ label = "supportsInterface" ];
}

  "SmartNft.<Constructor>";
  "SmartNft._setupRole";
  "SmartNft._setBaseURI";
  "SmartNft.INftfiHub";
  "SmartNft.setLoanCoordinator";
  "SmartNft.grantRole";
  "SmartNft.mint";
  "SmartNft._safeMint";
  "SmartNft.burn";
  "SmartNft._burn";
  "SmartNft.setBaseURI";
  "SmartNft.exists";
  "SmartNft._exists";
  "SmartNft.supportsInterface";
  "AccessControl.supportsInterface";
  "SmartNft._getChainID";
  "SmartNft.<Constructor>" -> "SmartNft._setupRole" [ color = "#1bc6a6" ];
  "SmartNft.<Constructor>" -> "SmartNft._setupRole" [ color = "#1bc6a6" ];
  "SmartNft.<Constructor>" -> "SmartNft._setupRole" [ color = "#1bc6a6" ];
  "SmartNft.<Constructor>" -> "SmartNft._setBaseURI" [ color = "#1bc6a6" ];
  "SmartNft.<Constructor>" -> "SmartNft.INftfiHub" [ color = "#1bc6a6" ];
  "SmartNft.setLoanCoordinator" -> "SmartNft.grantRole" [ color = "#1bc6a6" ];
  "SmartNft.mint" -> "SmartNft._safeMint" [ color = "#1bc6a6" ];
  "SmartNft.burn" -> "SmartNft._burn" [ color = "#1bc6a6" ];
  "SmartNft.setBaseURI" -> "SmartNft._setBaseURI" [ color = "#1bc6a6" ];
  "SmartNft.exists" -> "SmartNft._exists" [ color = "#1bc6a6" ];
  "SmartNft.supportsInterface" -> "AccessControl.supportsInterface" [ color = "white" ];
  "SmartNft._setBaseURI" -> "SmartNft._getChainID" [ color = "#1bc6a6" ];


rankdir=LR
node [shape=plaintext]
subgraph cluster_01 { 
label = "Legend";
key [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
  <tr><td align="right" port="i1">Internal Call</td></tr>
  <tr><td align="right" port="i2">External Call</td></tr>
  <tr><td align="right" port="i3">Defined Contract</td></tr>
  <tr><td align="right" port="i4">Undefined Contract</td></tr>
  </table>>]
key2 [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
  <tr><td port="i1">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i2">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i3" bgcolor="#445773">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i4">
    <table border="1" cellborder="0" cellspacing="0" cellpadding="7" color="#e8726d">
      <tr>
       <td></td>
      </tr>
     </table>
  </td></tr>
  </table>>]
key:i1:e -> key2:i1:w [color="#1bc6a6"]
key:i2:e -> key2:i2:w [color="white"]
}
}

NFTfiBundler

composable/NFTfiBundler.sol

digraph G {
  graph [ ratio = "auto", page = "100", compound =true, bgcolor = "#2e3e56" ];
  node [ style = "filled", fillcolor = "#edad56", color = "#edad56", penwidth =3 ];
  edge [ color = "#fcfcfc", penwidth =2, fontname = "helvetica Neue Ultra Light" ];
subgraph "clusterNftfiBundler" {
  graph [ label = "NftfiBundler", color = "#445773", fontcolor = "#f0f0f0", style = "rounded", bgcolor = "#445773" ];
  "NftfiBundler.<Constructor>" [ label = "<Constructor>", color = "#FF9797", fillcolor = "#FF9797" ];
  "NftfiBundler.supportsInterface" [ label = "supportsInterface", color = "#FF9797", fillcolor = "#FF9797" ];
  "NftfiBundler.permittedAsset" [ label = "permittedAsset", color = "#FF9797", fillcolor = "#FF9797" ];
  "NftfiBundler.permittedErc20Asset" [ label = "permittedErc20Asset", color = "#FF9797", fillcolor = "#FF9797" ];
  "NftfiBundler.buildBundle" [ label = "buildBundle", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "NftfiBundler.decomposeBundle" [ label = "decomposeBundle", color = "#ffbdb9", fillcolor = "#ffbdb9" ];
  "NftfiBundler._receiveChild" [ label = "_receiveChild", color = "#f2c383", fillcolor = "#f2c383" ];
  "NftfiBundler._receive1155Child" [ label = "_receive1155Child", color = "#f2c383", fillcolor = "#f2c383" ];
  "NftfiBundler._receiveErc20Child" [ label = "_receiveErc20Child", color = "#f2c383", fillcolor = "#f2c383" ];
  "NftfiBundler.INftfiHub" [ label = "INftfiHub" ];
  "NftfiBundler.type" [ label = "type" ];
  "NftfiBundler.IPermittedNFTs" [ label = "IPermittedNFTs" ];
  "NftfiBundler.IPermittedERC20s" [ label = "IPermittedERC20s" ];
  "NftfiBundler._safeMint" [ label = "_safeMint" ];
  "NftfiBundler.IERC721" [ label = "IERC721" ];
  "NftfiBundler._getChild" [ label = "_getChild" ];
  "NftfiBundler._getERC20" [ label = "_getERC20" ];
  "NftfiBundler.IERC1155" [ label = "IERC1155" ];
  "NftfiBundler.ownerOf" [ label = "ownerOf" ];
  "NftfiBundler._validateReceiver" [ label = "_validateReceiver" ];
  "NftfiBundler._remove1155Child" [ label = "_remove1155Child" ];
  "NftfiBundler.Transfer1155Child" [ label = "Transfer1155Child" ];
  "NftfiBundler._removeChild" [ label = "_removeChild" ];
  "NftfiBundler._oldNFTsTransfer" [ label = "_oldNFTsTransfer" ];
  "NftfiBundler.TransferChild" [ label = "TransferChild" ];
  "NftfiBundler._removeERC20" [ label = "_removeERC20" ];
  "NftfiBundler.IERC20" [ label = "IERC20" ];
  "NftfiBundler.TransferERC20" [ label = "TransferERC20" ];
}

subgraph "clusterERC998ERC20Extension" {
  graph [ label = "ERC998ERC20Extension", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "ERC998ERC20Extension.supportsInterface" [ label = "supportsInterface" ];
  "ERC998ERC20Extension._receiveChild" [ label = "_receiveChild" ];
  "ERC998ERC20Extension._receive1155Child" [ label = "_receive1155Child" ];
  "ERC998ERC20Extension._receiveErc20Child" [ label = "_receiveErc20Child" ];
}

subgraph "clusterINftfiHub" {
  graph [ label = "INftfiHub", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "INftfiHub.getContract" [ label = "getContract" ];
}

subgraph "clusterIPermittedNFTs" {
  graph [ label = "IPermittedNFTs", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "IPermittedNFTs.getNFTPermit" [ label = "getNFTPermit" ];
}

subgraph "clusterIPermittedERC20s" {
  graph [ label = "IPermittedERC20s", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ];
  "IPermittedERC20s.getERC20Permit" [ label = "getERC20Permit" ];
}

  "NftfiBundler.<Constructor>";
  "NftfiBundler.INftfiHub";
  "NftfiBundler.supportsInterface";
  "NftfiBundler.type";
  "ERC998ERC20Extension.supportsInterface";
  "NftfiBundler.permittedAsset";
  "NftfiBundler.IPermittedNFTs";
  "INftfiHub.getContract";
  "IPermittedNFTs.getNFTPermit";
  "NftfiBundler.permittedErc20Asset";
  "NftfiBundler.IPermittedERC20s";
  "IPermittedERC20s.getERC20Permit";
  "NftfiBundler.buildBundle";
  "NftfiBundler._safeMint";
  "NftfiBundler.IERC721";
  "NftfiBundler._getChild";
  "NftfiBundler._getERC20";
  "NftfiBundler.IERC1155";
  "NftfiBundler.decomposeBundle";
  "NftfiBundler.ownerOf";
  "NftfiBundler._validateReceiver";
  "NftfiBundler._remove1155Child";
  "NftfiBundler.Transfer1155Child";
  "NftfiBundler._removeChild";
  "NftfiBundler._oldNFTsTransfer";
  "NftfiBundler.TransferChild";
  "NftfiBundler._removeERC20";
  "NftfiBundler.IERC20";
  "NftfiBundler.TransferERC20";
  "NftfiBundler._receiveChild";
  "ERC998ERC20Extension._receiveChild";
  "NftfiBundler._receive1155Child";
  "ERC998ERC20Extension._receive1155Child";
  "NftfiBundler._receiveErc20Child";
  "ERC998ERC20Extension._receiveErc20Child";
  "NftfiBundler.<Constructor>" -> "NftfiBundler.INftfiHub" [ color = "#1bc6a6" ];
  "NftfiBundler.supportsInterface" -> "NftfiBundler.type" [ color = "#1bc6a6" ];
  "NftfiBundler.supportsInterface" -> "NftfiBundler.type" [ color = "#1bc6a6" ];
  "NftfiBundler.supportsInterface" -> "ERC998ERC20Extension.supportsInterface" [ color = "white" ];
  "NftfiBundler.permittedAsset" -> "NftfiBundler.IPermittedNFTs" [ color = "#1bc6a6" ];
  "NftfiBundler.permittedAsset" -> "INftfiHub.getContract" [ color = "white" ];
  "NftfiBundler.permittedAsset" -> "IPermittedNFTs.getNFTPermit" [ color = "white" ];
  "NftfiBundler.permittedErc20Asset" -> "NftfiBundler.IPermittedERC20s" [ color = "#1bc6a6" ];
  "NftfiBundler.permittedErc20Asset" -> "INftfiHub.getContract" [ color = "white" ];
  "NftfiBundler.permittedErc20Asset" -> "IPermittedERC20s.getERC20Permit" [ color = "white" ];
  "NftfiBundler.buildBundle" -> "NftfiBundler._safeMint" [ color = "#1bc6a6" ];
  "NftfiBundler.buildBundle" -> "NftfiBundler.IERC721" [ color = "#1bc6a6" ];
  "NftfiBundler.buildBundle" -> "NftfiBundler._getChild" [ color = "#1bc6a6" ];
  "NftfiBundler.buildBundle" -> "NftfiBundler._getERC20" [ color = "#1bc6a6" ];
  "NftfiBundler.buildBundle" -> "NftfiBundler.IERC1155" [ color = "#1bc6a6" ];
  "NftfiBundler.decomposeBundle" -> "NftfiBundler.ownerOf" [ color = "#1bc6a6" ];
  "NftfiBundler.decomposeBundle" -> "NftfiBundler._validateReceiver" [ color = "#1bc6a6" ];
  "NftfiBundler.decomposeBundle" -> "NftfiBundler._remove1155Child" [ color = "#1bc6a6" ];
  "NftfiBundler.decomposeBundle" -> "NftfiBundler.IERC1155" [ color = "#1bc6a6" ];
  "NftfiBundler.decomposeBundle" -> "NftfiBundler.Transfer1155Child" [ color = "#1bc6a6" ];
  "NftfiBundler.decomposeBundle" -> "NftfiBundler._removeChild" [ color = "#1bc6a6" ];
  "NftfiBundler.decomposeBundle" -> "NftfiBundler.IERC721" [ color = "#1bc6a6" ];
  "NftfiBundler.decomposeBundle" -> "NftfiBundler._oldNFTsTransfer" [ color = "#1bc6a6" ];
  "NftfiBundler.decomposeBundle" -> "NftfiBundler.TransferChild" [ color = "#1bc6a6" ];
  "NftfiBundler.decomposeBundle" -> "NftfiBundler._removeERC20" [ color = "#1bc6a6" ];
  "NftfiBundler.decomposeBundle" -> "NftfiBundler.IERC20" [ color = "#1bc6a6" ];
  "NftfiBundler.decomposeBundle" -> "NftfiBundler.TransferERC20" [ color = "#1bc6a6" ];
  "NftfiBundler._receiveChild" -> "NftfiBundler.permittedAsset" [ color = "#1bc6a6" ];
  "NftfiBundler._receiveChild" -> "ERC998ERC20Extension._receiveChild" [ color = "white" ];
  "NftfiBundler._receive1155Child" -> "NftfiBundler.permittedAsset" [ color = "#1bc6a6" ];
  "NftfiBundler._receive1155Child" -> "ERC998ERC20Extension._receive1155Child" [ color = "white" ];
  "NftfiBundler._receiveErc20Child" -> "NftfiBundler.permittedErc20Asset" [ color = "#1bc6a6" ];
  "NftfiBundler._receiveErc20Child" -> "ERC998ERC20Extension._receiveErc20Child" [ color = "white" ];


rankdir=LR
node [shape=plaintext]
subgraph cluster_01 { 
label = "Legend";
key [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
  <tr><td align="right" port="i1">Internal Call</td></tr>
  <tr><td align="right" port="i2">External Call</td></tr>
  <tr><td align="right" port="i3">Defined Contract</td></tr>
  <tr><td align="right" port="i4">Undefined Contract</td></tr>
  </table>>]
key2 [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
  <tr><td port="i1">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i2">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i3" bgcolor="#445773">&nbsp;&nbsp;&nbsp;</td></tr>
  <tr><td port="i4">
    <table border="1" cellborder="0" cellspacing="0" cellpadding="7" color="#e8726d">
      <tr>
       <td></td>
      </tr>
     </table>
  </td></tr>
  </table>>]
key:i1:e -> key2:i1:w [color="#1bc6a6"]
key:i2:e -> key2:i2:w [color="white"]
}
}

10. Automated Testing

STATIC ANALYSIS REPORT

Description

Halborn used automated testing techniques to enhance coverage of certain areas of the scoped contract. Among the tools used was Slither, a Solidity static analysis framework. After Halborn verified all the contracts in the repository and was able to compile them correctly into their ABI and binary formats. This tool can statically verify mathematical relationships between Solidity variables to detect invalid or inconsistent usage of the contracts' APIs across the entire code-base.

Results

contracts/loans/direct/contracts_loans_direct_DirectLoanCoordinator_slithercontracts/loans/direct/loanTypes/contracts_loans_direct_loanTypes_DirectLoanFixedOffer_slithercontracts/loans/direct/loanTypes/contracts_loans_direct_loanTypes_LoanAirdropUtils_slithercontracts/loans/direct/loanTypes/contracts_loans_direct_loanTypes_DirectLoanBaseMinimal_slithercontracts/smartNft/contracts_smartNft_SmartNft_slithercontracts/composable/contracts_composable_NftfiBundler_slithercontracts/composable/contracts_composable_ERC998TopDown_slithercontracts/composable/contracts_composable_ERC9981155Extension_slithercontracts/composable/contracts_composable_ERC998ERC20Extension_slithercontracts/nftTypeRegistry/nftTypes/contracts_nftTypeRegistry_nftTypes_CryptoKittiesWrapper_slithercontracts/airdrop/contracts_airdrop_AirdropReceiverFactory_slithercontracts/airdrop/contracts_airdrop_AirdropFlashLoan_slithercontracts/airdrop/contracts_airdrop_AirdropReceiver_slither

According to the test results, the findings found by these tools were considered false positives. All relevant findings were reviewed by the auditors, and relevant findings were addressed in the report as security concerns.

Halborn strongly recommends conducting a follow-up assessment of the project either within six months or immediately following any material changes to the codebase, whichever comes first. This approach is crucial for maintaining the project’s integrity and addressing potential vulnerabilities introduced by code modifications.

// Download the full report

* Use Google Chrome for best results

** Check "Background Graphics" in the print settings if needed

© Halborn 2024. All rights reserved.