Flow

Sablier
FoundryDeFi
20,000 USDC
View results
Submission Details
Severity: medium
Invalid

Large Static SVG Storage in Contract Code Inflates Deployment Costs and Blockchain Storage Overhead

Summary

The FlowNFTDescriptor contract stores a large SVG string directly in the contract code, which gets included in every deployment and stays permanently in blockchain storage.

https://github.com/Cyfrin/2024-10-sablier/blob/main/src/FlowNFTDescriptor.sol#L13

function tokenURI(
IERC721Metadata, /* sablierFlow */
uint256 /* streamId */
)
external
pure
override
returns (string memory uri)
{
// Large static SVG stored directly in contract
string memory svg =
'<svg width="500" height="500" style="background-color: #14161F;" xmlns="http://www.w3.org/2000/svg" viewBox="20 -400 200 1000"><path id="Logo" fill="#fff" fill-opacity="1" d="m133.559,124.034c-.013,2.412-1.059,4.848-2.923,6.402-2.558,1.819-5.168,3.439-7.888,4.996-14.44,8.262-31.047,12.565-47.674,12.569-8.858.036-17.838-1.272-26.328-3.663-9.806-2.766-19.087-7.113-27.562-12.778-13.842-8.025,9.468-28.606,16.153-35.265h0c2.035-1.838,4.252-3.546,6.463-5.224h0c6.429-5.655,16.218-2.835,20.358,4.17,4.143,5.057,8.816,9.649,13.92,13.734h.037c5.736,6.461,15.357-2.253,9.38-8.48,0,0-3.515-3.515-3.515-3.515-11.49-11.478-52.656-52.664-64.837-64.837l.049-.037c-1.725-1.606-2.719-3.847-2.751-6.204h0c-.046-2.375,1.062-4.582,2.726-6.229h0l.185-.148h0c.099-.062,.222-.148,.37-.259h0c2.06-1.362,3.951-2.621,6.044-3.842C57.763-3.473,97.76-2.341,128.637,18.332c16.671,9.946-26.344,54.813-38.651,40.199-6.299-6.096-18.063-17.743-19.668-18.811-6.016-4.047-13.061,4.776-7.752,9.751l68.254,68.371c1.724,1.601,2.714,3.84,2.738,6.192Z" transform="scale(1.5, 1.5)" /></svg>';
}

Impact

The large static SVG data in the contract creates several critical issues:

Deployment costs become prohibitively expensive as each byte of contract code costs 200 gas. With approximately 1KB of SVG data, this alone adds about 200,000 gas (~$40-100 depending on gas price) to each deployment. For protocols that require multiple deployments across different networks or frequent upgrades, these costs compound significantly.

Every node in the network must permanently store this data in their state trie, multiplying the storage burden across the entire Ethereum network. While a single contract's storage might seem insignificant, this becomes a substantial scaling concern when considering widespread adoption of the protocol.

When the protocol needs updating, the entire SVG must be redeployed in a new contract, incurring the full gas cost again. The tests show the NFTDescriptor is upgradeable, but the current implementation makes these upgrades unnecessarily expensive.

Fix

contract FlowNFTDescriptor is IFlowNFTDescriptor {
// Store SVG in smaller, reusable components
bytes32 private constant SVG_HEADER = "...";
bytes32 private constant SVG_FOOTER = "...";
bytes32 private constant PATH_START = "...";
// Break down the path data into smaller chunks
bytes32[] private constant LOGO_PATHS = [
"m133.559,124.034c-.013,2.412...",
"-1.059,4.848-2.923,6.402...",
// ...more manageable chunks
];
function tokenURI(
IERC721Metadata sablierFlow,
uint256 streamId
)
external
view
override
returns (string memory uri)
{
// Efficiently construct SVG from components
string memory svg = string(
abi.encodePacked(
SVG_HEADER,
_constructLogoPath(),
SVG_FOOTER
)
);
// Rest of the implementation...
}
function _constructLogoPath() internal pure returns (string memory) {
// Efficiently concatenate path data
return string(
abi.encodePacked(
PATH_START,
LOGO_PATHS[0],
LOGO_PATHS[1]
// ...rest of paths
)
);
}
}

Alternative fix using initialization:

contract FlowNFTDescriptor is IFlowNFTDescriptor {
string private immutable logoSVG;
constructor() {
// Split construction over multiple steps to optimize deployment
string memory part1 = "...";
string memory part2 = "...";
logoSVG = string(abi.encodePacked(part1, part2));
}
function tokenURI(
IERC721Metadata sablierFlow,
uint256 streamId
)
external
view
override
returns (string memory uri)
{
// Use stored SVG
return _constructURI(logoSVG);
}
}

Additional Recommendations

  1. Consider storing SVG components off-chain and implement gas-efficient assembly for URIs

  2. Use SVG optimization tools to reduce path data size

  3. Implement progressive loading for larger graphics

  4. Consider a factory pattern for cheaper deployments

These changes would significantly reduce deployment costs and make upgrades more economical while maintaining the same functionality.

Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.