From 2467cda48237d6ddbfc9b63476d5733f45e50296 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Wed, 11 Dec 2024 22:57:24 +0530 Subject: [PATCH] perf: optimize NFTDescriptor contract feat: remove backward compatibility Co-authored-by: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> --- script/GenerateSVG.s.sol | 1 - src/LockupNFTDescriptor.sol | 54 +--- src/libraries/NFTSVG.sol | 11 +- tests/fork/NFTDescriptor.t.sol | 282 ------------------ tests/integration/Integration.t.sol | 2 +- .../lockup-base/token-uri/tokenURI.t.sol | 4 +- .../nft-descriptor/map-symbol/mapSymbol.t.sol | 24 -- .../nft-descriptor/map-symbol/mapSymbol.tree | 5 - tests/mocks/NFTDescriptorMock.sol | 12 +- .../nft-descriptor/generateDescription.t.sol | 32 +- .../nft-descriptor/generateName.t.sol | 37 --- .../concrete/nft-descriptor/generateSVG.t.sol | 9 +- 12 files changed, 27 insertions(+), 446 deletions(-) delete mode 100644 tests/fork/NFTDescriptor.t.sol delete mode 100644 tests/integration/concrete/nft-descriptor/map-symbol/mapSymbol.t.sol delete mode 100644 tests/integration/concrete/nft-descriptor/map-symbol/mapSymbol.tree delete mode 100644 tests/unit/concrete/nft-descriptor/generateName.t.sol diff --git a/script/GenerateSVG.s.sol b/script/GenerateSVG.s.sol index 6b1bac990..3b2d0a225 100644 --- a/script/GenerateSVG.s.sol +++ b/script/GenerateSVG.s.sol @@ -39,7 +39,6 @@ contract GenerateSVG is BaseScript, LockupNFTDescriptor { progress: stringifyPercentage(progress), progressNumerical: progress, lockupAddress: LOCKUP.toHexString(), - lockupModel: "Lockup Linear", status: status }) ); diff --git a/src/LockupNFTDescriptor.sol b/src/LockupNFTDescriptor.sol index a0de93d42..d206a468f 100644 --- a/src/LockupNFTDescriptor.sol +++ b/src/LockupNFTDescriptor.sol @@ -9,10 +9,10 @@ import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { ILockupNFTDescriptor } from "./interfaces/ILockupNFTDescriptor.sol"; import { ISablierLockup } from "./interfaces/ISablierLockup.sol"; import { ISablierLockupBase } from "./interfaces/ISablierLockupBase.sol"; -import { Errors } from "./libraries/Errors.sol"; import { NFTSVG } from "./libraries/NFTSVG.sol"; import { SVGElements } from "./libraries/SVGElements.sol"; import { Lockup } from "./types/DataTypes.sol"; + /* ██╗ ██████╗ ██████╗██╗ ██╗██╗ ██╗██████╗ ███╗ ██╗███████╗████████╗ @@ -50,7 +50,6 @@ contract LockupNFTDescriptor is ILockupNFTDescriptor { bool isTransferable; string json; ISablierLockup lockup; - string lockupModel; string lockupStringified; bytes returnData; string status; @@ -65,22 +64,12 @@ contract LockupNFTDescriptor is ILockupNFTDescriptor { // Load the contracts. vars.lockup = ISablierLockup(address(lockup)); - vars.lockupModel = mapSymbol(lockup); vars.lockupStringified = address(lockup).toHexString(); - vars.tokenSymbol = safeTokenSymbol(vars.token); vars.depositedAmount = vars.lockup.getDepositedAmount(streamId); // Retrieve the underlying token contract's address. - if (vars.lockupModel.equal("Sablier Lockup")) { - // For Lockup contract versions v2.0.0 and later, use the `getUnderlyingToken` function. - vars.token = address(vars.lockup.getUnderlyingToken(streamId)); - } - // For Lockup contract versions earlier than v2.0.0, use the `getAsset` function. - else { - (, bytes memory returnData) = - address(lockup).staticcall(abi.encodeWithSignature("getAsset(uint256)", streamId)); - vars.token = abi.decode(returnData, (address)); - } + vars.token = address(vars.lockup.getUnderlyingToken(streamId)); + vars.tokenSymbol = safeTokenSymbol(vars.token); // Load the stream's data. vars.status = stringifyStatus(vars.lockup.statusOf(streamId)); @@ -103,8 +92,7 @@ contract LockupNFTDescriptor is ILockupNFTDescriptor { lockupAddress: vars.lockupStringified, progress: stringifyPercentage(vars.streamedPercentage), progressNumerical: vars.streamedPercentage, - status: vars.status, - lockupModel: vars.lockupModel + status: vars.status }) ); @@ -125,7 +113,6 @@ contract LockupNFTDescriptor is ILockupNFTDescriptor { }), ',"description":"', generateDescription({ - lockupModel: vars.lockupModel, tokenSymbol: vars.tokenSymbol, lockupStringified: vars.lockupStringified, tokenAddress: vars.token.toHexString(), @@ -133,7 +120,7 @@ contract LockupNFTDescriptor is ILockupNFTDescriptor { isTransferable: vars.isTransferable }), '","external_url":"https://sablier.com","name":"', - generateName({ lockupModel: vars.lockupModel, streamId: streamId.toString() }), + string.concat("Sablier Lockup #", streamId.toString()), '","image":"data:image/svg+xml;base64,', Base64.encode(bytes(vars.svg)), '"}' @@ -286,7 +273,6 @@ contract LockupNFTDescriptor is ILockupNFTDescriptor { /// @notice Generates a string with the NFT's JSON metadata description, which provides a high-level overview. function generateDescription( - string memory lockupModel, string memory tokenSymbol, string memory lockupStringified, string memory tokenAddress, @@ -304,15 +290,12 @@ contract LockupNFTDescriptor is ILockupNFTDescriptor { : unicode"❕INFO: This NFT is non-transferable. It cannot be sold or transferred to another account."; return string.concat( - "This NFT represents a payment stream in a Sablier Lockup ", - lockupModel, - " contract. The owner of this NFT can withdraw the streamed tokens, which are denominated in ", + "This NFT represents a stream in Sablier Lockup contract. The owner of this NFT can withdraw the streamed tokens, which are denominated in ", tokenSymbol, ".\\n\\n- Stream ID: ", streamId, "\\n- ", - lockupModel, - " Address: ", + "Sablier Lockup Address: ", lockupStringified, "\\n- ", tokenSymbol, @@ -323,12 +306,6 @@ contract LockupNFTDescriptor is ILockupNFTDescriptor { ); } - /// @notice Generates a string with the NFT's JSON metadata name, which is unique for each stream. - /// @dev The `streamId` is equivalent to the ERC-721 `tokenId`. - function generateName(string memory lockupModel, string memory streamId) internal pure returns (string memory) { - return string.concat("Sablier ", lockupModel, " #", streamId); - } - /// @notice Checks whether the provided string contains only alphanumeric characters, spaces, and dashes. /// @dev Note that this returns true for empty strings. function isAllowedCharacter(string memory str) internal pure returns (bool) { @@ -352,23 +329,6 @@ contract LockupNFTDescriptor is ILockupNFTDescriptor { return true; } - /// @notice Maps ERC-721 symbols to human-readable model names. - /// @dev Reverts if the symbol is unknown. - function mapSymbol(IERC721Metadata sablier) internal view returns (string memory) { - string memory symbol = sablier.symbol(); - if (symbol.equal("SAB-LOCKUP")) { - return "Sablier Lockup"; - } else if (symbol.equal("SAB-LOCKUP-LIN") || symbol.equal("SAB-V2-LOCKUP-LIN")) { - return "Sablier Lockup Linear"; - } else if (symbol.equal("SAB-LOCKUP-DYN") || symbol.equal("SAB-V2-LOCKUP-DYN")) { - return "Sablier Lockup Dynamic"; - } else if (symbol.equal("SAB-LOCKUP-TRA") || symbol.equal("SAB-V2-LOCKUP-TRA")) { - return "Sablier Lockup Tranched"; - } else { - revert Errors.LockupNFTDescriptor_UnknownNFT(sablier, symbol); - } - } - /// @notice Retrieves the token's decimals safely, defaulting to "0" if an error occurs. /// @dev Performs a low-level call to handle tokens in which the decimals are not implemented. function safeTokenDecimals(address token) internal view returns (uint8) { diff --git a/src/libraries/NFTSVG.sol b/src/libraries/NFTSVG.sol index 5bbf0dd0a..d6e233743 100644 --- a/src/libraries/NFTSVG.sol +++ b/src/libraries/NFTSVG.sol @@ -18,7 +18,6 @@ library NFTSVG { string tokenSymbol; string duration; string lockupAddress; - string lockupModel; string progress; uint256 progressNumerical; string status; @@ -89,7 +88,7 @@ library NFTSVG { '', SVGElements.BACKGROUND, generateDefs(params.accentColor, params.status, vars.cards), - generateFloatingText(params.lockupAddress, params.lockupModel, params.tokenAddress, params.tokenSymbol), + generateFloatingText(params.lockupAddress, params.tokenAddress, params.tokenSymbol), generateHrefs(vars.progressXPosition, vars.statusXPosition, vars.amountXPosition, vars.durationXPosition), "" ); @@ -119,7 +118,6 @@ library NFTSVG { function generateFloatingText( string memory lockupAddress, - string memory lockupModel, string memory tokenAddress, string memory tokenSymbol ) @@ -131,12 +129,9 @@ library NFTSVG { '', SVGElements.floatingText({ offset: "-100%", - text: string.concat(lockupAddress, unicode" • ", "Sablier ", lockupModel) - }), - SVGElements.floatingText({ - offset: "0%", - text: string.concat(lockupAddress, unicode" • ", "Sablier ", lockupModel) + text: string.concat(lockupAddress, unicode" • ", "Sablier Lockup") }), + SVGElements.floatingText({ offset: "0%", text: string.concat(lockupAddress, unicode" • ", "Sablier Lockup") }), SVGElements.floatingText({ offset: "-50%", text: string.concat(tokenAddress, unicode" • ", tokenSymbol) }), SVGElements.floatingText({ offset: "50%", text: string.concat(tokenAddress, unicode" • ", tokenSymbol) }), "" diff --git a/tests/fork/NFTDescriptor.t.sol b/tests/fork/NFTDescriptor.t.sol deleted file mode 100644 index 0ea8fce46..000000000 --- a/tests/fork/NFTDescriptor.t.sol +++ /dev/null @@ -1,282 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.22 <0.9.0; - -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -import { ISablierLockup } from "src/interfaces/ISablierLockup.sol"; - -import { Fork_Test } from "./Fork.t.sol"; - -contract NFTDescriptor_Fork_Test is Fork_Test { - /*////////////////////////////////////////////////////////////////////////// - STATE VARIABLES - //////////////////////////////////////////////////////////////////////////*/ - - IERC20 internal constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); - address internal constant DAI_HOLDER = 0x66F62574ab04989737228D18C3624f7FC1edAe14; - - ISablierLockup internal lockupDynamic; - ISablierLockup internal lockupLinear; - ISablierLockup internal lockupTranched; - - /*////////////////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////////////////*/ - - constructor() Fork_Test(DAI, DAI_HOLDER) { } - - /*////////////////////////////////////////////////////////////////////////// - MODIFIERS - //////////////////////////////////////////////////////////////////////////*/ - - /// @dev Loads the Lockup v1.0.0 contracts pre-deployed on Mainnet. - modifier loadDeployments_v1_0_0() { - lockupDynamic = ISablierLockup(0x39EFdC3dbB57B2388CcC4bb40aC4CB1226Bc9E44); - lockupLinear = ISablierLockup(0xB10daee1FCF62243aE27776D7a92D39dC8740f95); - _; - } - - /// @dev Loads the Lockup v1.1.2 contracts pre-deployed on Mainnet. - modifier loadDeployments_v1_1_2() { - lockupDynamic = ISablierLockup(0x7CC7e125d83A581ff438608490Cc0f7bDff79127); - lockupLinear = ISablierLockup(0xAFb979d9afAd1aD27C5eFf4E27226E3AB9e5dCC9); - _; - } - - /// @dev Loads the Lockup v1.2.0 contracts pre-deployed on Mainnet. - modifier loadDeployments_v1_2_0() { - lockupDynamic = ISablierLockup(0x9DeaBf7815b42Bf4E9a03EEc35a486fF74ee7459); - lockupLinear = ISablierLockup(0x3962f6585946823440d274aD7C719B02b49DE51E); - lockupTranched = ISablierLockup(0xf86B359035208e4529686A1825F2D5BeE38c28A8); - _; - } - - /// @dev Loads the Lockup v1.3.0 contracts pre-deployed on Mainnet. - modifier loadDeployments_v1_3_0() { - // TODO: Add the deployment addresses for Lockup v1.3.0. - // Deploy some streams temporarity for the test - resetPrank({ msgSender: users.sender }); - lockup.createWithDurationsLL(defaults.createWithDurations(), defaults.unlockAmounts(), defaults.durations()); - _; - } - - /*////////////////////////////////////////////////////////////////////////// - SET-UP FUNCTION - //////////////////////////////////////////////////////////////////////////*/ - - function setUp() public virtual override { - Fork_Test.setUp(); - } - - /*////////////////////////////////////////////////////////////////////////// - TEST FUNCTIONS - //////////////////////////////////////////////////////////////////////////*/ - - /// @dev The following test checks whether the new NFT descriptor is compatible with Lockup Dynamic v1.0.0. - /// - /// Checklist: - /// - It should expect a call to {ISablierLockup.tokenURI}. - /// - The test would fail if the call to {ISablierLockup.tokenURI} reverts. - /// - /// Given enough fuzz runs, all the following scenarios will be fuzzed: - /// - Multiple values of streamId. - function testForkFuzz_TokenURI_Lockup_Dynamic_v1_0_0(uint256 streamId) external loadDeployments_v1_0_0 { - streamId = _bound(streamId, 1, lockupDynamic.nextStreamId() - 1); - - // Set the new NFT descriptor for the previous version of Lockup Dynamic. - resetPrank({ msgSender: lockupDynamic.admin() }); - lockupDynamic.setNFTDescriptor(nftDescriptor); - - // Expects a successful call to the new NFT Descriptor. - vm.expectCall({ - callee: address(nftDescriptor), - data: abi.encodeCall(nftDescriptor.tokenURI, (lockupDynamic, streamId)), - count: 1 - }); - - // Generate the token URI using the new NFT Descriptor. - lockupDynamic.tokenURI(streamId); - } - - /// @dev The following test checks whether the new NFT descriptor is compatible with Lockup Dynamic v1.1.2. - /// - /// Checklist: - /// - It should expect a call to {ISablierLockup.tokenURI}. - /// - The test would fail if the call to {ISablierLockup.tokenURI} reverts. - /// - /// Given enough fuzz runs, all the following scenarios will be fuzzed: - /// - Multiple values of streamId. - function testForkFuzz_TokenURI_Lockup_Dynamic_v1_1_2(uint256 streamId) external loadDeployments_v1_1_2 { - streamId = _bound(streamId, 1, lockupDynamic.nextStreamId() - 1); - - // Set the new NFT descriptor for the previous version of Lockup Dynamic. - resetPrank({ msgSender: lockupDynamic.admin() }); - lockupDynamic.setNFTDescriptor(nftDescriptor); - - // Expects a successful call to the new NFT Descriptor. - vm.expectCall({ - callee: address(nftDescriptor), - data: abi.encodeCall(nftDescriptor.tokenURI, (lockupDynamic, streamId)), - count: 1 - }); - - // Generate the token URI using the new NFT Descriptor. - lockupDynamic.tokenURI(streamId); - } - - /// @dev The following test checks whether the new NFT descriptor is compatible with Lockup Dynamic v1.2.0. - /// - /// Checklist: - /// - It should expect a call to {ISablierLockup.tokenURI}. - /// - The test would fail if the call to {ISablierLockup.tokenURI} reverts. - /// - /// Given enough fuzz runs, all the following scenarios will be fuzzed: - /// - Multiple values of streamId. - function testForkFuzz_TokenURI_Lockup_Dynamic_v1_2_0(uint256 streamId) external loadDeployments_v1_2_0 { - streamId = _bound(streamId, 1, lockupDynamic.nextStreamId() - 1); - - // Set the new NFT descriptor for the previous version of Lockup Dynamic. - resetPrank({ msgSender: lockupDynamic.admin() }); - lockupDynamic.setNFTDescriptor(nftDescriptor); - - // Expects a successful call to the new NFT Descriptor. - vm.expectCall({ - callee: address(nftDescriptor), - data: abi.encodeCall(nftDescriptor.tokenURI, (lockupDynamic, streamId)), - count: 1 - }); - - // Generate the token URI using the new NFT Descriptor. - lockupDynamic.tokenURI(streamId); - } - - /// @dev The following test checks whether the new NFT descriptor is compatible with Lockup Linear v1.0.0. - /// - /// Checklist: - /// - It should expect a call to {ISablierLockup.tokenURI}. - /// - The test would fail if the call to {ISablierLockup.tokenURI} reverts. - /// - /// Given enough fuzz runs, all the following scenarios will be fuzzed: - /// - Multiple values of streamId. - function testForkFuzz_TokenURI_Lockup_Linear_v1_0_0(uint256 streamId) external loadDeployments_v1_0_0 { - streamId = _bound(streamId, 1, lockupLinear.nextStreamId() - 1); - - // Set the new NFT descriptor for the previous version of Lockup Linear. - resetPrank({ msgSender: lockupLinear.admin() }); - lockupLinear.setNFTDescriptor(nftDescriptor); - - // Expects a successful call to the new NFT Descriptor. - vm.expectCall({ - callee: address(nftDescriptor), - data: abi.encodeCall(nftDescriptor.tokenURI, (lockupLinear, streamId)), - count: 1 - }); - - // Generate the token URI using the new NFT Descriptor. - lockupLinear.tokenURI(streamId); - } - - /// @dev The following test checks whether the new NFT descriptor is compatible with Lockup Linear v1.1.2. - /// - /// Checklist: - /// - It should expect a call to {ISablierLockup.tokenURI}. - /// - The test would fail if the call to {ISablierLockup.tokenURI} reverts. - /// - /// Given enough fuzz runs, all the following scenarios will be fuzzed: - /// - Multiple values of streamId. - function testForkFuzz_TokenURI_Lockup_Linear_v1_1_2(uint256 streamId) external loadDeployments_v1_1_2 { - streamId = _bound(streamId, 1, lockupLinear.nextStreamId() - 1); - - // Set the new NFT descriptor for the previous version of Lockup Linear. - resetPrank({ msgSender: lockupLinear.admin() }); - lockupLinear.setNFTDescriptor(nftDescriptor); - - // Expects a successful call to the new NFT Descriptor. - vm.expectCall({ - callee: address(nftDescriptor), - data: abi.encodeCall(nftDescriptor.tokenURI, (lockupLinear, streamId)), - count: 1 - }); - - // Generate the token URI using the new NFT Descriptor. - lockupLinear.tokenURI(streamId); - } - - /// @dev The following test checks whether the new NFT descriptor is compatible with Lockup Linear v1.2.0. - /// - /// Checklist: - /// - It should expect a call to {ISablierLockup.tokenURI}. - /// - The test would fail if the call to {ISablierLockup.tokenURI} reverts. - /// - /// Given enough fuzz runs, all the following scenarios will be fuzzed: - /// - Multiple values of streamId. - function testForkFuzz_TokenURI_Lockup_Linear_v1_2_0(uint256 streamId) external loadDeployments_v1_2_0 { - streamId = _bound(streamId, 1, lockupLinear.nextStreamId() - 1); - - // Set the new NFT descriptor for the previous version of Lockup Linear. - resetPrank({ msgSender: lockupLinear.admin() }); - lockupLinear.setNFTDescriptor(nftDescriptor); - - // Expects a successful call to the new NFT Descriptor. - vm.expectCall({ - callee: address(nftDescriptor), - data: abi.encodeCall(nftDescriptor.tokenURI, (lockupLinear, streamId)), - count: 1 - }); - - // Generate the token URI using the new NFT Descriptor. - lockupLinear.tokenURI(streamId); - } - - /// @dev The following test checks whether the new NFT descriptor is compatible with Lockup Tranched v1.2.0. - /// - /// Checklist: - /// - It should expect a call to {ISablierLockup.tokenURI}. - /// - The test would fail if the call to {ISablierLockup.tokenURI} reverts. - /// - /// Given enough fuzz runs, all the following scenarios will be fuzzed: - /// - Multiple values of streamId. - function testForkFuzz_TokenURI_Lockup_Tranched_v1_2_0(uint256 streamId) external loadDeployments_v1_2_0 { - streamId = _bound(streamId, 1, lockupTranched.nextStreamId() - 1); - - // Set the new NFT descriptor for the previous version of Lockup Tranched. - resetPrank({ msgSender: lockupTranched.admin() }); - lockupTranched.setNFTDescriptor(nftDescriptor); - - // Expects a successful call to the new NFT Descriptor. - vm.expectCall({ - callee: address(nftDescriptor), - data: abi.encodeCall(nftDescriptor.tokenURI, (lockupTranched, streamId)), - count: 1 - }); - - // Generate the token URI using the new NFT Descriptor. - lockupTranched.tokenURI(streamId); - } - - /// @dev The following test checks whether the new NFT descriptor is compatible with Lockup v1.3.0. - /// - /// Checklist: - /// - It should expect a call to {ISablierLockup.tokenURI}. - /// - The test would fail if the call to {ISablierLockup.tokenURI} reverts. - /// - /// Given enough fuzz runs, all the following scenarios will be fuzzed: - /// - Multiple values of streamId. - function testForkFuzz_TokenURI_Lockup_v1_3_0(uint256 streamId) external loadDeployments_v1_3_0 { - streamId = _bound(streamId, 1, lockup.nextStreamId() - 1); - - // Set the new NFT descriptor for the previous version of Lockup. - resetPrank({ msgSender: lockup.admin() }); - lockup.setNFTDescriptor(nftDescriptor); - - // Expects a successful call to the new NFT Descriptor. - vm.expectCall({ - callee: address(nftDescriptor), - data: abi.encodeCall(nftDescriptor.tokenURI, (lockup, streamId)), - count: 1 - }); - - // Generate the token URI using the new NFT Descriptor. - lockup.tokenURI(streamId); - } -} diff --git a/tests/integration/Integration.t.sol b/tests/integration/Integration.t.sol index 01c3a68ba..640fe1729 100644 --- a/tests/integration/Integration.t.sol +++ b/tests/integration/Integration.t.sol @@ -36,7 +36,7 @@ abstract contract Integration_Test is Base_Test { uint256 internal recipientInvalidSelectorStreamId; // A stream with a reentrant contract as the recipient. uint256 internal recipientReentrantStreamId; - // Astream with a reverting contract as the stream's recipient. + // A stream with a reverting contract as the stream's recipient. uint256 internal recipientRevertStreamId; struct CreateParams { diff --git a/tests/integration/concrete/lockup-base/token-uri/tokenURI.t.sol b/tests/integration/concrete/lockup-base/token-uri/tokenURI.t.sol index e8a37c309..7069afbcf 100644 --- a/tests/integration/concrete/lockup-base/token-uri/tokenURI.t.sol +++ b/tests/integration/concrete/lockup-base/token-uri/tokenURI.t.sol @@ -43,7 +43,7 @@ contract TokenURI_Lockup_Integration_Concrete_Test is Integration_Test { tokenURI = vm.replace({ input: tokenURI, from: "data:application/json;base64,", to: "" }); string memory actualDecodedTokenURI = string(Base64.decode(tokenURI)); string memory expectedDecodedTokenURI = - unicode'{"attributes":[{"trait_type":"Token","value":"ERC20"},{"trait_type":"Sender","value":"0x6332e7b1deb1f1a0b77b2bb18b144330c7291bca"},{"trait_type":"Status","value":"Streaming"}],"description":"This NFT represents a payment stream in a Sablier Lockup Sablier Lockup contract. The owner of this NFT can withdraw the streamed tokens, which are denominated in ERC20.\\n\\n- Stream ID: 1\\n- Sablier Lockup Address: 0x923b5ab3714fd343316af5a5434582fd16722523\\n- ERC20 Address: 0xf62849f9a0b5bf2913b396098f7c7019b51a820a\\n\\n⚠️ WARNING: Transferring the NFT makes the new owner the recipient of the stream. The funds are not automatically withdrawn for the previous recipient.","external_url":"https://sablier.com","name":"Sablier Sablier Lockup #1","image":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAwIiBoZWlnaHQ9IjEwMDAiIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbHRlcj0idXJsKCNOb2lzZSkiLz48cmVjdCB4PSI3MCIgeT0iNzAiIHdpZHRoPSI4NjAiIGhlaWdodD0iODYwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4wMyIgcng9IjQ1IiByeT0iNDUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLW9wYWNpdHk9Ii4xIiBzdHJva2Utd2lkdGg9IjQiLz48ZGVmcz48Y2lyY2xlIGlkPSJHbG93IiByPSI1MDAiIGZpbGw9InVybCgjUmFkaWFsR2xvdykiLz48ZmlsdGVyIGlkPSJOb2lzZSI+PGZlRmxvb2QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmxvb2QtY29sb3I9ImhzbCgyMzAsMjElLDExJSkiIGZsb29kLW9wYWNpdHk9IjEiIHJlc3VsdD0iZmxvb2RGaWxsIi8+PGZlVHVyYnVsZW5jZSBiYXNlRnJlcXVlbmN5PSIuNCIgbnVtT2N0YXZlcz0iMyIgcmVzdWx0PSJOb2lzZSIgdHlwZT0iZnJhY3RhbE5vaXNlIi8+PGZlQmxlbmQgaW49Ik5vaXNlIiBpbjI9ImZsb29kRmlsbCIgbW9kZT0ic29mdC1saWdodCIvPjwvZmlsdGVyPjxwYXRoIGlkPSJMb2dvIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4xIiBkPSJtMTMzLjU1OSwxMjQuMDM0Yy0uMDEzLDIuNDEyLTEuMDU5LDQuODQ4LTIuOTIzLDYuNDAyLTIuNTU4LDEuODE5LTUuMTY4LDMuNDM5LTcuODg4LDQuOTk2LTE0LjQ0LDguMjYyLTMxLjA0NywxMi41NjUtNDcuNjc0LDEyLjU2OS04Ljg1OC4wMzYtMTcuODM4LTEuMjcyLTI2LjMyOC0zLjY2My05LjgwNi0yLjc2Ni0xOS4wODctNy4xMTMtMjcuNTYyLTEyLjc3OC0xMy44NDItOC4wMjUsOS40NjgtMjguNjA2LDE2LjE1My0zNS4yNjVoMGMyLjAzNS0xLjgzOCw0LjI1Mi0zLjU0Niw2LjQ2My01LjIyNGgwYzYuNDI5LTUuNjU1LDE2LjIxOC0yLjgzNSwyMC4zNTgsNC4xNyw0LjE0Myw1LjA1Nyw4LjgxNiw5LjY0OSwxMy45MiwxMy43MzRoLjAzN2M1LjczNiw2LjQ2MSwxNS4zNTctMi4yNTMsOS4zOC04LjQ4LDAsMC0zLjUxNS0zLjUxNS0zLjUxNS0zLjUxNS0xMS40OS0xMS40NzgtNTIuNjU2LTUyLjY2NC02NC44MzctNjQuODM3bC4wNDktLjAzN2MtMS43MjUtMS42MDYtMi43MTktMy44NDctMi43NTEtNi4yMDRoMGMtLjA0Ni0yLjM3NSwxLjA2Mi00LjU4MiwyLjcyNi02LjIyOWgwbC4xODUtLjE0OGgwYy4wOTktLjA2MiwuMjIyLS4xNDgsLjM3LS4yNTloMGMyLjA2LTEuMzYyLDMuOTUxLTIuNjIxLDYuMDQ0LTMuODQyQzU3Ljc2My0zLjQ3Myw5Ny43Ni0yLjM0MSwxMjguNjM3LDE4LjMzMmMxNi42NzEsOS45NDYtMjYuMzQ0LDU0LjgxMy0zOC42NTEsNDAuMTk5LTYuMjk5LTYuMDk2LTE4LjA2My0xNy43NDMtMTkuNjY4LTE4LjgxMS02LjAxNi00LjA0Ny0xMy4wNjEsNC43NzYtNy43NTIsOS43NTFsNjguMjU0LDY4LjM3MWMxLjcyNCwxLjYwMSwyLjcxNCwzLjg0LDIuNzM4LDYuMTkyWiIvPjxwYXRoIGlkPSJGbG9hdGluZ1RleHQiIGZpbGw9Im5vbmUiIGQ9Ik0xMjUgNDVoNzUwczgwIDAgODAgODB2NzUwczAgODAgLTgwIDgwaC03NTBzLTgwIDAgLTgwIC04MHYtNzUwczAgLTgwIDgwIC04MCIvPjxyYWRpYWxHcmFkaWVudCBpZD0iUmFkaWFsR2xvdyI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iaHNsKDYzLDk1JSw1NyUpIiBzdG9wLW9wYWNpdHk9Ii42Ii8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSJoc2woMjMwLDIxJSwxMSUpIiBzdG9wLW9wYWNpdHk9IjAiLz48L3JhZGlhbEdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iU2FuZFRvcCIgeDE9IjAlIiB5MT0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9ImhzbCg2Myw5NSUsNTclKSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iaHNsKDIzMCwyMSUsMTElKSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJTYW5kQm90dG9tIiB4MT0iMTAwJSIgeTE9IjEwMCUiPjxzdG9wIG9mZnNldD0iMTAlIiBzdG9wLWNvbG9yPSJoc2woMjMwLDIxJSwxMSUpIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSJoc2woNjMsOTUlLDU3JSkiLz48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ4MSIgZHVyPSI2cyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHZhbHVlcz0iMzAlOzYwJTsxMjAlOzYwJTszMCU7Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9IkhvdXJnbGFzc1N0cm9rZSIgZ3JhZGllbnRUcmFuc2Zvcm09InJvdGF0ZSg5MCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iaHNsKDYzLDk1JSw1NyUpIi8+PHN0b3Agb2Zmc2V0PSI4MCUiIHN0b3AtY29sb3I9ImhzbCgyMzAsMjElLDExJSkiLz48L2xpbmVhckdyYWRpZW50PjxnIGlkPSJIb3VyZ2xhc3MiPjxwYXRoIGQ9Ik0gNTAsMzYwIGEgMzAwLDMwMCAwIDEsMSA2MDAsMCBhIDMwMCwzMDAgMCAxLDEgLTYwMCwwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4wMiIgc3Ryb2tlPSJ1cmwoI0hvdXJnbGFzc1N0cm9rZSkiIHN0cm9rZS13aWR0aD0iNCIvPjxwYXRoIGQ9Im01NjYsMTYxLjIwMXYtNTMuOTI0YzAtMTkuMzgyLTIyLjUxMy0zNy41NjMtNjMuMzk4LTUxLjE5OC00MC43NTYtMTMuNTkyLTk0Ljk0Ni0yMS4wNzktMTUyLjU4Ny0yMS4wNzlzLTExMS44MzgsNy40ODctMTUyLjYwMiwyMS4wNzljLTQwLjg5MywxMy42MzYtNjMuNDEzLDMxLjgxNi02My40MTMsNTEuMTk4djUzLjkyNGMwLDE3LjE4MSwxNy43MDQsMzMuNDI3LDUwLjIyMyw0Ni4zOTR2Mjg0LjgwOWMtMzIuNTE5LDEyLjk2LTUwLjIyMywyOS4yMDYtNTAuMjIzLDQ2LjM5NHY1My45MjRjMCwxOS4zODIsMjIuNTIsMzcuNTYzLDYzLjQxMyw1MS4xOTgsNDAuNzYzLDEzLjU5Miw5NC45NTQsMjEuMDc5LDE1Mi42MDIsMjEuMDc5czExMS44MzEtNy40ODcsMTUyLjU4Ny0yMS4wNzljNDAuODg2LTEzLjYzNiw2My4zOTgtMzEuODE2LDYzLjM5OC01MS4xOTh2LTUzLjkyNGMwLTE3LjE5Ni0xNy43MDQtMzMuNDM1LTUwLjIyMy00Ni40MDFWMjA3LjYwM2MzMi41MTktMTIuOTY3LDUwLjIyMy0yOS4yMDYsNTAuMjIzLTQ2LjQwMVptLTM0Ny40NjIsNTcuNzkzbDEzMC45NTksMTMxLjAyNy0xMzAuOTU5LDEzMS4wMTNWMjE4Ljk5NFptMjYyLjkyNC4wMjJ2MjYyLjAxOGwtMTMwLjkzNy0xMzEuMDA2LDEzMC45MzctMTMxLjAxM1oiIGZpbGw9IiMxNjE4MjIiPjwvcGF0aD48cG9seWdvbiBwb2ludHM9IjM1MCAzNTAuMDI2IDQxNS4wMyAyODQuOTc4IDI4NSAyODQuOTc4IDM1MCAzNTAuMDI2IiBmaWxsPSJ1cmwoI1NhbmRCb3R0b20pIi8+PHBhdGggZD0ibTQxNi4zNDEsMjgxLjk3NWMwLC45MTQtLjM1NCwxLjgwOS0xLjAzNSwyLjY4LTUuNTQyLDcuMDc2LTMyLjY2MSwxMi40NS02NS4yOCwxMi40NS0zMi42MjQsMC01OS43MzgtNS4zNzQtNjUuMjgtMTIuNDUtLjY4MS0uODcyLTEuMDM1LTEuNzY3LTEuMDM1LTIuNjgsMC0uOTE0LjM1NC0xLjgwOCwxLjAzNS0yLjY3Niw1LjU0Mi03LjA3NiwzMi42NTYtMTIuNDUsNjUuMjgtMTIuNDUsMzIuNjE5LDAsNTkuNzM4LDUuMzc0LDY1LjI4LDEyLjQ1LjY4MS44NjcsMS4wMzUsMS43NjIsMS4wMzUsMi42NzZaIiBmaWxsPSJ1cmwoI1NhbmRUb3ApIi8+PHBhdGggZD0ibTQ4MS40Niw1MDQuMTAxdjU4LjQ0OWMtMi4zNS43Ny00LjgyLDEuNTEtNy4zOSwyLjIzLTMwLjMsOC41NC03NC42NSwxMy45Mi0xMjQuMDYsMTMuOTItNTMuNiwwLTEwMS4yNC02LjMzLTEzMS40Ny0xNi4xNnYtNTguNDM5aDI2Mi45MloiIGZpbGw9InVybCgjU2FuZEJvdHRvbSkiLz48ZWxsaXBzZSBjeD0iMzUwIiBjeT0iNTA0LjEwMSIgcng9IjEzMS40NjIiIHJ5PSIyOC4xMDgiIGZpbGw9InVybCgjU2FuZFRvcCkiLz48ZyBmaWxsPSJub25lIiBzdHJva2U9InVybCgjSG91cmdsYXNzU3Ryb2tlKSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iNCI+PHBhdGggZD0ibTU2NS42NDEsMTA3LjI4YzAsOS41MzctNS41NiwxOC42MjktMTUuNjc2LDI2Ljk3M2gtLjAyM2MtOS4yMDQsNy41OTYtMjIuMTk0LDE0LjU2Mi0zOC4xOTcsMjAuNTkyLTM5LjUwNCwxNC45MzYtOTcuMzI1LDI0LjM1NS0xNjEuNzMzLDI0LjM1NS05MC40OCwwLTE2Ny45NDgtMTguNTgyLTE5OS45NTMtNDQuOTQ4aC0uMDIzYy0xMC4xMTUtOC4zNDQtMTUuNjc2LTE3LjQzNy0xNS42NzYtMjYuOTczLDAtMzkuNzM1LDk2LjU1NC03MS45MjEsMjE1LjY1Mi03MS45MjFzMjE1LjYyOSwzMi4xODUsMjE1LjYyOSw3MS45MjFaIi8+PHBhdGggZD0ibTEzNC4zNiwxNjEuMjAzYzAsMzkuNzM1LDk2LjU1NCw3MS45MjEsMjE1LjY1Miw3MS45MjFzMjE1LjYyOS0zMi4xODYsMjE1LjYyOS03MS45MjEiLz48bGluZSB4MT0iMTM0LjM2IiB5MT0iMTYxLjIwMyIgeDI9IjEzNC4zNiIgeTI9IjEwNy4yOCIvPjxsaW5lIHgxPSI1NjUuNjQiIHkxPSIxNjEuMjAzIiB4Mj0iNTY1LjY0IiB5Mj0iMTA3LjI4Ii8+PGxpbmUgeDE9IjE4NC41ODQiIHkxPSIyMDYuODIzIiB4Mj0iMTg0LjU4NSIgeTI9IjUzNy41NzkiLz48bGluZSB4MT0iMjE4LjE4MSIgeTE9IjIxOC4xMTgiIHgyPSIyMTguMTgxIiB5Mj0iNTYyLjUzNyIvPjxsaW5lIHgxPSI0ODEuODE4IiB5MT0iMjE4LjE0MiIgeDI9IjQ4MS44MTkiIHkyPSI1NjIuNDI4Ii8+PGxpbmUgeDE9IjUxNS40MTUiIHkxPSIyMDcuMzUyIiB4Mj0iNTE1LjQxNiIgeTI9IjUzNy41NzkiLz48cGF0aCBkPSJtMTg0LjU4LDUzNy41OGMwLDUuNDUsNC4yNywxMC42NSwxMi4wMywxNS40MmguMDJjNS41MSwzLjM5LDEyLjc5LDYuNTUsMjEuNTUsOS40MiwzMC4yMSw5LjksNzguMDIsMTYuMjgsMTMxLjgzLDE2LjI4LDQ5LjQxLDAsOTMuNzYtNS4zOCwxMjQuMDYtMTMuOTIsMi43LS43Niw1LjI5LTEuNTQsNy43NS0yLjM1LDguNzctMi44NywxNi4wNS02LjA0LDIxLjU2LTkuNDNoMGM3Ljc2LTQuNzcsMTIuMDQtOS45NywxMi4wNC0xNS40MiIvPjxwYXRoIGQ9Im0xODQuNTgyLDQ5Mi42NTZjLTMxLjM1NCwxMi40ODUtNTAuMjIzLDI4LjU4LTUwLjIyMyw0Ni4xNDIsMCw5LjUzNiw1LjU2NCwxOC42MjcsMTUuNjc3LDI2Ljk2OWguMDIyYzguNTAzLDcuMDA1LDIwLjIxMywxMy40NjMsMzQuNTI0LDE5LjE1OSw5Ljk5OSwzLjk5MSwyMS4yNjksNy42MDksMzMuNTk3LDEwLjc4OCwzNi40NSw5LjQwNyw4Mi4xODEsMTUuMDAyLDEzMS44MzUsMTUuMDAyczk1LjM2My01LjU5NSwxMzEuODA3LTE1LjAwMmMxMC44NDctMi43OSwyMC44NjctNS45MjYsMjkuOTI0LTkuMzQ5LDEuMjQ0LS40NjcsMi40NzMtLjk0MiwzLjY3My0xLjQyNCwxNC4zMjYtNS42OTYsMjYuMDM1LTEyLjE2MSwzNC41MjQtMTkuMTczaC4wMjJjMTAuMTE0LTguMzQyLDE1LjY3Ny0xNy40MzMsMTUuNjc3LTI2Ljk2OSwwLTE3LjU2Mi0xOC44NjktMzMuNjY1LTUwLjIyMy00Ni4xNSIvPjxwYXRoIGQ9Im0xMzQuMzYsNTkyLjcyYzAsMzkuNzM1LDk2LjU1NCw3MS45MjEsMjE1LjY1Miw3MS45MjFzMjE1LjYyOS0zMi4xODYsMjE1LjYyOS03MS45MjEiLz48bGluZSB4MT0iMTM0LjM2IiB5MT0iNTkyLjcyIiB4Mj0iMTM0LjM2IiB5Mj0iNTM4Ljc5NyIvPjxsaW5lIHgxPSI1NjUuNjQiIHkxPSI1OTIuNzIiIHgyPSI1NjUuNjQiIHkyPSI1MzguNzk3Ii8+PHBvbHlsaW5lIHBvaW50cz0iNDgxLjgyMiA0ODEuOTAxIDQ4MS43OTggNDgxLjg3NyA0ODEuNzc1IDQ4MS44NTQgMzUwLjAxNSAzNTAuMDI2IDIxOC4xODUgMjE4LjEyOSIvPjxwb2x5bGluZSBwb2ludHM9IjIxOC4xODUgNDgxLjkwMSAyMTguMjMxIDQ4MS44NTQgMzUwLjAxNSAzNTAuMDI2IDQ4MS44MjIgMjE4LjE1MiIvPjwvZz48L2c+PGcgaWQ9IlByb2dyZXNzIiBmaWxsPSIjZmZmIj48cmVjdCB3aWR0aD0iMjA4IiBoZWlnaHQ9IjEwMCIgZmlsbC1vcGFjaXR5PSIuMDMiIHJ4PSIxNSIgcnk9IjE1IiBzdHJva2U9IiNmZmYiIHN0cm9rZS1vcGFjaXR5PSIuMSIgc3Ryb2tlLXdpZHRoPSI0Ii8+PHRleHQgeD0iMjAiIHk9IjM0IiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjJweCI+UHJvZ3Jlc3M8L3RleHQ+PHRleHQgeD0iMjAiIHk9IjcyIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjZweCI+MjIuOTglPC90ZXh0PjxnIGZpbGw9Im5vbmUiPjxjaXJjbGUgY3g9IjE2NiIgY3k9IjUwIiByPSIyMiIgc3Ryb2tlPSJoc2woMjMwLDIxJSwxMSUpIiBzdHJva2Utd2lkdGg9IjEwIi8+PGNpcmNsZSBjeD0iMTY2IiBjeT0iNTAiIHBhdGhMZW5ndGg9IjEwMDAwIiByPSIyMiIgc3Ryb2tlPSJoc2woNjMsOTUlLDU3JSkiIHN0cm9rZS1kYXNoYXJyYXk9IjEwMDAwIiBzdHJva2UtZGFzaG9mZnNldD0iNzcwMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjUiIHRyYW5zZm9ybT0icm90YXRlKC05MCkiIHRyYW5zZm9ybS1vcmlnaW49IjE2NiA1MCIvPjwvZz48L2c+PGcgaWQ9IlN0YXR1cyIgZmlsbD0iI2ZmZiI+PHJlY3Qgd2lkdGg9IjE4NCIgaGVpZ2h0PSIxMDAiIGZpbGwtb3BhY2l0eT0iLjAzIiByeD0iMTUiIHJ5PSIxNSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZS13aWR0aD0iNCIvPjx0ZXh0IHg9IjIwIiB5PSIzNCIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjIycHgiPlN0YXR1czwvdGV4dD48dGV4dCB4PSIyMCIgeT0iNzIiIGZvbnQtZmFtaWx5PSInQ291cmllciBOZXcnLEFyaWFsLG1vbm9zcGFjZSIgZm9udC1zaXplPSIyNnB4Ij5TdHJlYW1pbmc8L3RleHQ+PC9nPjxnIGlkPSJBbW91bnQiIGZpbGw9IiNmZmYiPjxyZWN0IHdpZHRoPSIxMjAiIGhlaWdodD0iMTAwIiBmaWxsLW9wYWNpdHk9Ii4wMyIgcng9IjE1IiByeT0iMTUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLW9wYWNpdHk9Ii4xIiBzdHJva2Utd2lkdGg9IjQiLz48dGV4dCB4PSIyMCIgeT0iMzQiIGZvbnQtZmFtaWx5PSInQ291cmllciBOZXcnLEFyaWFsLG1vbm9zcGFjZSIgZm9udC1zaXplPSIyMnB4Ij5BbW91bnQ8L3RleHQ+PHRleHQgeD0iMjAiIHk9IjcyIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjZweCI+JiM4ODA1OyAxMEs8L3RleHQ+PC9nPjxnIGlkPSJEdXJhdGlvbiIgZmlsbD0iI2ZmZiI+PHJlY3Qgd2lkdGg9IjE1MiIgaGVpZ2h0PSIxMDAiIGZpbGwtb3BhY2l0eT0iLjAzIiByeD0iMTUiIHJ5PSIxNSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZS13aWR0aD0iNCIvPjx0ZXh0IHg9IjIwIiB5PSIzNCIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjIycHgiPkR1cmF0aW9uPC90ZXh0Pjx0ZXh0IHg9IjIwIiB5PSI3MiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjI2cHgiPiZsdDsgMSBEYXk8L3RleHQ+PC9nPjwvZGVmcz48dGV4dCB0ZXh0LXJlbmRlcmluZz0ib3B0aW1pemVTcGVlZCI+PHRleHRQYXRoIHN0YXJ0T2Zmc2V0PSItMTAwJSIgaHJlZj0iI0Zsb2F0aW5nVGV4dCIgZmlsbD0iI2ZmZiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmaWxsLW9wYWNpdHk9Ii44IiBmb250LXNpemU9IjI2cHgiPjxhbmltYXRlIGFkZGl0aXZlPSJzdW0iIGF0dHJpYnV0ZU5hbWU9InN0YXJ0T2Zmc2V0IiBiZWdpbj0iMHMiIGR1cj0iNTBzIiBmcm9tPSIwJSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHRvPSIxMDAlIi8+MHg5MjNiNWFiMzcxNGZkMzQzMzE2YWY1YTU0MzQ1ODJmZDE2NzIyNTIzIOKAoiBTYWJsaWVyIFNhYmxpZXIgTG9ja3VwPC90ZXh0UGF0aD48dGV4dFBhdGggc3RhcnRPZmZzZXQ9IjAlIiBocmVmPSIjRmxvYXRpbmdUZXh0IiBmaWxsPSIjZmZmIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZpbGwtb3BhY2l0eT0iLjgiIGZvbnQtc2l6ZT0iMjZweCI+PGFuaW1hdGUgYWRkaXRpdmU9InN1bSIgYXR0cmlidXRlTmFtZT0ic3RhcnRPZmZzZXQiIGJlZ2luPSIwcyIgZHVyPSI1MHMiIGZyb209IjAlIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgdG89IjEwMCUiLz4weDkyM2I1YWIzNzE0ZmQzNDMzMTZhZjVhNTQzNDU4MmZkMTY3MjI1MjMg4oCiIFNhYmxpZXIgU2FibGllciBMb2NrdXA8L3RleHRQYXRoPjx0ZXh0UGF0aCBzdGFydE9mZnNldD0iLTUwJSIgaHJlZj0iI0Zsb2F0aW5nVGV4dCIgZmlsbD0iI2ZmZiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmaWxsLW9wYWNpdHk9Ii44IiBmb250LXNpemU9IjI2cHgiPjxhbmltYXRlIGFkZGl0aXZlPSJzdW0iIGF0dHJpYnV0ZU5hbWU9InN0YXJ0T2Zmc2V0IiBiZWdpbj0iMHMiIGR1cj0iNTBzIiBmcm9tPSIwJSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHRvPSIxMDAlIi8+MHhmNjI4NDlmOWEwYjViZjI5MTNiMzk2MDk4ZjdjNzAxOWI1MWE4MjBhIOKAoiBFUkMyMDwvdGV4dFBhdGg+PHRleHRQYXRoIHN0YXJ0T2Zmc2V0PSI1MCUiIGhyZWY9IiNGbG9hdGluZ1RleHQiIGZpbGw9IiNmZmYiIGZvbnQtZmFtaWx5PSInQ291cmllciBOZXcnLEFyaWFsLG1vbm9zcGFjZSIgZmlsbC1vcGFjaXR5PSIuOCIgZm9udC1zaXplPSIyNnB4Ij48YW5pbWF0ZSBhZGRpdGl2ZT0ic3VtIiBhdHRyaWJ1dGVOYW1lPSJzdGFydE9mZnNldCIgYmVnaW49IjBzIiBkdXI9IjUwcyIgZnJvbT0iMCUiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB0bz0iMTAwJSIvPjB4ZjYyODQ5ZjlhMGI1YmYyOTEzYjM5NjA5OGY3YzcwMTliNTFhODIwYSDigKIgRVJDMjA8L3RleHRQYXRoPjwvdGV4dD48dXNlIGhyZWY9IiNHbG93IiBmaWxsLW9wYWNpdHk9Ii45Ii8+PHVzZSBocmVmPSIjR2xvdyIgeD0iMTAwMCIgeT0iMTAwMCIgZmlsbC1vcGFjaXR5PSIuOSIvPjx1c2UgaHJlZj0iI0xvZ28iIHg9IjE3MCIgeT0iMTcwIiB0cmFuc2Zvcm09InNjYWxlKC42KSIvPjx1c2UgaHJlZj0iI0hvdXJnbGFzcyIgeD0iMTUwIiB5PSI5MCIgdHJhbnNmb3JtPSJyb3RhdGUoMTApIiB0cmFuc2Zvcm0tb3JpZ2luPSI1MDAgNTAwIi8+PHVzZSBocmVmPSIjUHJvZ3Jlc3MiIHg9IjE0NCIgeT0iNzkwIi8+PHVzZSBocmVmPSIjU3RhdHVzIiB4PSIzNjgiIHk9Ijc5MCIvPjx1c2UgaHJlZj0iI0Ftb3VudCIgeD0iNTY4IiB5PSI3OTAiLz48dXNlIGhyZWY9IiNEdXJhdGlvbiIgeD0iNzA0IiB5PSI3OTAiLz48L3N2Zz4="}'; + unicode'{"attributes":[{"trait_type":"Token","value":"DAI"},{"trait_type":"Sender","value":"0x6332e7b1deb1f1a0b77b2bb18b144330c7291bca"},{"trait_type":"Status","value":"Streaming"}],"description":"This NFT represents a stream in Sablier Lockup contract. The owner of this NFT can withdraw the streamed tokens, which are denominated in DAI.\\n\\n- Stream ID: 1\\n- Sablier Lockup Address: 0x923b5ab3714fd343316af5a5434582fd16722523\\n- DAI Address: 0xf62849f9a0b5bf2913b396098f7c7019b51a820a\\n\\n⚠️ WARNING: Transferring the NFT makes the new owner the recipient of the stream. The funds are not automatically withdrawn for the previous recipient.","external_url":"https://sablier.com","name":"Sablier Lockup #1","image":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAwIiBoZWlnaHQ9IjEwMDAiIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbHRlcj0idXJsKCNOb2lzZSkiLz48cmVjdCB4PSI3MCIgeT0iNzAiIHdpZHRoPSI4NjAiIGhlaWdodD0iODYwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4wMyIgcng9IjQ1IiByeT0iNDUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLW9wYWNpdHk9Ii4xIiBzdHJva2Utd2lkdGg9IjQiLz48ZGVmcz48Y2lyY2xlIGlkPSJHbG93IiByPSI1MDAiIGZpbGw9InVybCgjUmFkaWFsR2xvdykiLz48ZmlsdGVyIGlkPSJOb2lzZSI+PGZlRmxvb2QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmxvb2QtY29sb3I9ImhzbCgyMzAsMjElLDExJSkiIGZsb29kLW9wYWNpdHk9IjEiIHJlc3VsdD0iZmxvb2RGaWxsIi8+PGZlVHVyYnVsZW5jZSBiYXNlRnJlcXVlbmN5PSIuNCIgbnVtT2N0YXZlcz0iMyIgcmVzdWx0PSJOb2lzZSIgdHlwZT0iZnJhY3RhbE5vaXNlIi8+PGZlQmxlbmQgaW49Ik5vaXNlIiBpbjI9ImZsb29kRmlsbCIgbW9kZT0ic29mdC1saWdodCIvPjwvZmlsdGVyPjxwYXRoIGlkPSJMb2dvIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4xIiBkPSJtMTMzLjU1OSwxMjQuMDM0Yy0uMDEzLDIuNDEyLTEuMDU5LDQuODQ4LTIuOTIzLDYuNDAyLTIuNTU4LDEuODE5LTUuMTY4LDMuNDM5LTcuODg4LDQuOTk2LTE0LjQ0LDguMjYyLTMxLjA0NywxMi41NjUtNDcuNjc0LDEyLjU2OS04Ljg1OC4wMzYtMTcuODM4LTEuMjcyLTI2LjMyOC0zLjY2My05LjgwNi0yLjc2Ni0xOS4wODctNy4xMTMtMjcuNTYyLTEyLjc3OC0xMy44NDItOC4wMjUsOS40NjgtMjguNjA2LDE2LjE1My0zNS4yNjVoMGMyLjAzNS0xLjgzOCw0LjI1Mi0zLjU0Niw2LjQ2My01LjIyNGgwYzYuNDI5LTUuNjU1LDE2LjIxOC0yLjgzNSwyMC4zNTgsNC4xNyw0LjE0Myw1LjA1Nyw4LjgxNiw5LjY0OSwxMy45MiwxMy43MzRoLjAzN2M1LjczNiw2LjQ2MSwxNS4zNTctMi4yNTMsOS4zOC04LjQ4LDAsMC0zLjUxNS0zLjUxNS0zLjUxNS0zLjUxNS0xMS40OS0xMS40NzgtNTIuNjU2LTUyLjY2NC02NC44MzctNjQuODM3bC4wNDktLjAzN2MtMS43MjUtMS42MDYtMi43MTktMy44NDctMi43NTEtNi4yMDRoMGMtLjA0Ni0yLjM3NSwxLjA2Mi00LjU4MiwyLjcyNi02LjIyOWgwbC4xODUtLjE0OGgwYy4wOTktLjA2MiwuMjIyLS4xNDgsLjM3LS4yNTloMGMyLjA2LTEuMzYyLDMuOTUxLTIuNjIxLDYuMDQ0LTMuODQyQzU3Ljc2My0zLjQ3Myw5Ny43Ni0yLjM0MSwxMjguNjM3LDE4LjMzMmMxNi42NzEsOS45NDYtMjYuMzQ0LDU0LjgxMy0zOC42NTEsNDAuMTk5LTYuMjk5LTYuMDk2LTE4LjA2My0xNy43NDMtMTkuNjY4LTE4LjgxMS02LjAxNi00LjA0Ny0xMy4wNjEsNC43NzYtNy43NTIsOS43NTFsNjguMjU0LDY4LjM3MWMxLjcyNCwxLjYwMSwyLjcxNCwzLjg0LDIuNzM4LDYuMTkyWiIvPjxwYXRoIGlkPSJGbG9hdGluZ1RleHQiIGZpbGw9Im5vbmUiIGQ9Ik0xMjUgNDVoNzUwczgwIDAgODAgODB2NzUwczAgODAgLTgwIDgwaC03NTBzLTgwIDAgLTgwIC04MHYtNzUwczAgLTgwIDgwIC04MCIvPjxyYWRpYWxHcmFkaWVudCBpZD0iUmFkaWFsR2xvdyI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iaHNsKDYzLDk1JSw1NyUpIiBzdG9wLW9wYWNpdHk9Ii42Ii8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSJoc2woMjMwLDIxJSwxMSUpIiBzdG9wLW9wYWNpdHk9IjAiLz48L3JhZGlhbEdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iU2FuZFRvcCIgeDE9IjAlIiB5MT0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9ImhzbCg2Myw5NSUsNTclKSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iaHNsKDIzMCwyMSUsMTElKSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJTYW5kQm90dG9tIiB4MT0iMTAwJSIgeTE9IjEwMCUiPjxzdG9wIG9mZnNldD0iMTAlIiBzdG9wLWNvbG9yPSJoc2woMjMwLDIxJSwxMSUpIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSJoc2woNjMsOTUlLDU3JSkiLz48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ4MSIgZHVyPSI2cyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHZhbHVlcz0iMzAlOzYwJTsxMjAlOzYwJTszMCU7Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9IkhvdXJnbGFzc1N0cm9rZSIgZ3JhZGllbnRUcmFuc2Zvcm09InJvdGF0ZSg5MCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iaHNsKDYzLDk1JSw1NyUpIi8+PHN0b3Agb2Zmc2V0PSI4MCUiIHN0b3AtY29sb3I9ImhzbCgyMzAsMjElLDExJSkiLz48L2xpbmVhckdyYWRpZW50PjxnIGlkPSJIb3VyZ2xhc3MiPjxwYXRoIGQ9Ik0gNTAsMzYwIGEgMzAwLDMwMCAwIDEsMSA2MDAsMCBhIDMwMCwzMDAgMCAxLDEgLTYwMCwwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4wMiIgc3Ryb2tlPSJ1cmwoI0hvdXJnbGFzc1N0cm9rZSkiIHN0cm9rZS13aWR0aD0iNCIvPjxwYXRoIGQ9Im01NjYsMTYxLjIwMXYtNTMuOTI0YzAtMTkuMzgyLTIyLjUxMy0zNy41NjMtNjMuMzk4LTUxLjE5OC00MC43NTYtMTMuNTkyLTk0Ljk0Ni0yMS4wNzktMTUyLjU4Ny0yMS4wNzlzLTExMS44MzgsNy40ODctMTUyLjYwMiwyMS4wNzljLTQwLjg5MywxMy42MzYtNjMuNDEzLDMxLjgxNi02My40MTMsNTEuMTk4djUzLjkyNGMwLDE3LjE4MSwxNy43MDQsMzMuNDI3LDUwLjIyMyw0Ni4zOTR2Mjg0LjgwOWMtMzIuNTE5LDEyLjk2LTUwLjIyMywyOS4yMDYtNTAuMjIzLDQ2LjM5NHY1My45MjRjMCwxOS4zODIsMjIuNTIsMzcuNTYzLDYzLjQxMyw1MS4xOTgsNDAuNzYzLDEzLjU5Miw5NC45NTQsMjEuMDc5LDE1Mi42MDIsMjEuMDc5czExMS44MzEtNy40ODcsMTUyLjU4Ny0yMS4wNzljNDAuODg2LTEzLjYzNiw2My4zOTgtMzEuODE2LDYzLjM5OC01MS4xOTh2LTUzLjkyNGMwLTE3LjE5Ni0xNy43MDQtMzMuNDM1LTUwLjIyMy00Ni40MDFWMjA3LjYwM2MzMi41MTktMTIuOTY3LDUwLjIyMy0yOS4yMDYsNTAuMjIzLTQ2LjQwMVptLTM0Ny40NjIsNTcuNzkzbDEzMC45NTksMTMxLjAyNy0xMzAuOTU5LDEzMS4wMTNWMjE4Ljk5NFptMjYyLjkyNC4wMjJ2MjYyLjAxOGwtMTMwLjkzNy0xMzEuMDA2LDEzMC45MzctMTMxLjAxM1oiIGZpbGw9IiMxNjE4MjIiPjwvcGF0aD48cG9seWdvbiBwb2ludHM9IjM1MCAzNTAuMDI2IDQxNS4wMyAyODQuOTc4IDI4NSAyODQuOTc4IDM1MCAzNTAuMDI2IiBmaWxsPSJ1cmwoI1NhbmRCb3R0b20pIi8+PHBhdGggZD0ibTQxNi4zNDEsMjgxLjk3NWMwLC45MTQtLjM1NCwxLjgwOS0xLjAzNSwyLjY4LTUuNTQyLDcuMDc2LTMyLjY2MSwxMi40NS02NS4yOCwxMi40NS0zMi42MjQsMC01OS43MzgtNS4zNzQtNjUuMjgtMTIuNDUtLjY4MS0uODcyLTEuMDM1LTEuNzY3LTEuMDM1LTIuNjgsMC0uOTE0LjM1NC0xLjgwOCwxLjAzNS0yLjY3Niw1LjU0Mi03LjA3NiwzMi42NTYtMTIuNDUsNjUuMjgtMTIuNDUsMzIuNjE5LDAsNTkuNzM4LDUuMzc0LDY1LjI4LDEyLjQ1LjY4MS44NjcsMS4wMzUsMS43NjIsMS4wMzUsMi42NzZaIiBmaWxsPSJ1cmwoI1NhbmRUb3ApIi8+PHBhdGggZD0ibTQ4MS40Niw1MDQuMTAxdjU4LjQ0OWMtMi4zNS43Ny00LjgyLDEuNTEtNy4zOSwyLjIzLTMwLjMsOC41NC03NC42NSwxMy45Mi0xMjQuMDYsMTMuOTItNTMuNiwwLTEwMS4yNC02LjMzLTEzMS40Ny0xNi4xNnYtNTguNDM5aDI2Mi45MloiIGZpbGw9InVybCgjU2FuZEJvdHRvbSkiLz48ZWxsaXBzZSBjeD0iMzUwIiBjeT0iNTA0LjEwMSIgcng9IjEzMS40NjIiIHJ5PSIyOC4xMDgiIGZpbGw9InVybCgjU2FuZFRvcCkiLz48ZyBmaWxsPSJub25lIiBzdHJva2U9InVybCgjSG91cmdsYXNzU3Ryb2tlKSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iNCI+PHBhdGggZD0ibTU2NS42NDEsMTA3LjI4YzAsOS41MzctNS41NiwxOC42MjktMTUuNjc2LDI2Ljk3M2gtLjAyM2MtOS4yMDQsNy41OTYtMjIuMTk0LDE0LjU2Mi0zOC4xOTcsMjAuNTkyLTM5LjUwNCwxNC45MzYtOTcuMzI1LDI0LjM1NS0xNjEuNzMzLDI0LjM1NS05MC40OCwwLTE2Ny45NDgtMTguNTgyLTE5OS45NTMtNDQuOTQ4aC0uMDIzYy0xMC4xMTUtOC4zNDQtMTUuNjc2LTE3LjQzNy0xNS42NzYtMjYuOTczLDAtMzkuNzM1LDk2LjU1NC03MS45MjEsMjE1LjY1Mi03MS45MjFzMjE1LjYyOSwzMi4xODUsMjE1LjYyOSw3MS45MjFaIi8+PHBhdGggZD0ibTEzNC4zNiwxNjEuMjAzYzAsMzkuNzM1LDk2LjU1NCw3MS45MjEsMjE1LjY1Miw3MS45MjFzMjE1LjYyOS0zMi4xODYsMjE1LjYyOS03MS45MjEiLz48bGluZSB4MT0iMTM0LjM2IiB5MT0iMTYxLjIwMyIgeDI9IjEzNC4zNiIgeTI9IjEwNy4yOCIvPjxsaW5lIHgxPSI1NjUuNjQiIHkxPSIxNjEuMjAzIiB4Mj0iNTY1LjY0IiB5Mj0iMTA3LjI4Ii8+PGxpbmUgeDE9IjE4NC41ODQiIHkxPSIyMDYuODIzIiB4Mj0iMTg0LjU4NSIgeTI9IjUzNy41NzkiLz48bGluZSB4MT0iMjE4LjE4MSIgeTE9IjIxOC4xMTgiIHgyPSIyMTguMTgxIiB5Mj0iNTYyLjUzNyIvPjxsaW5lIHgxPSI0ODEuODE4IiB5MT0iMjE4LjE0MiIgeDI9IjQ4MS44MTkiIHkyPSI1NjIuNDI4Ii8+PGxpbmUgeDE9IjUxNS40MTUiIHkxPSIyMDcuMzUyIiB4Mj0iNTE1LjQxNiIgeTI9IjUzNy41NzkiLz48cGF0aCBkPSJtMTg0LjU4LDUzNy41OGMwLDUuNDUsNC4yNywxMC42NSwxMi4wMywxNS40MmguMDJjNS41MSwzLjM5LDEyLjc5LDYuNTUsMjEuNTUsOS40MiwzMC4yMSw5LjksNzguMDIsMTYuMjgsMTMxLjgzLDE2LjI4LDQ5LjQxLDAsOTMuNzYtNS4zOCwxMjQuMDYtMTMuOTIsMi43LS43Niw1LjI5LTEuNTQsNy43NS0yLjM1LDguNzctMi44NywxNi4wNS02LjA0LDIxLjU2LTkuNDNoMGM3Ljc2LTQuNzcsMTIuMDQtOS45NywxMi4wNC0xNS40MiIvPjxwYXRoIGQ9Im0xODQuNTgyLDQ5Mi42NTZjLTMxLjM1NCwxMi40ODUtNTAuMjIzLDI4LjU4LTUwLjIyMyw0Ni4xNDIsMCw5LjUzNiw1LjU2NCwxOC42MjcsMTUuNjc3LDI2Ljk2OWguMDIyYzguNTAzLDcuMDA1LDIwLjIxMywxMy40NjMsMzQuNTI0LDE5LjE1OSw5Ljk5OSwzLjk5MSwyMS4yNjksNy42MDksMzMuNTk3LDEwLjc4OCwzNi40NSw5LjQwNyw4Mi4xODEsMTUuMDAyLDEzMS44MzUsMTUuMDAyczk1LjM2My01LjU5NSwxMzEuODA3LTE1LjAwMmMxMC44NDctMi43OSwyMC44NjctNS45MjYsMjkuOTI0LTkuMzQ5LDEuMjQ0LS40NjcsMi40NzMtLjk0MiwzLjY3My0xLjQyNCwxNC4zMjYtNS42OTYsMjYuMDM1LTEyLjE2MSwzNC41MjQtMTkuMTczaC4wMjJjMTAuMTE0LTguMzQyLDE1LjY3Ny0xNy40MzMsMTUuNjc3LTI2Ljk2OSwwLTE3LjU2Mi0xOC44NjktMzMuNjY1LTUwLjIyMy00Ni4xNSIvPjxwYXRoIGQ9Im0xMzQuMzYsNTkyLjcyYzAsMzkuNzM1LDk2LjU1NCw3MS45MjEsMjE1LjY1Miw3MS45MjFzMjE1LjYyOS0zMi4xODYsMjE1LjYyOS03MS45MjEiLz48bGluZSB4MT0iMTM0LjM2IiB5MT0iNTkyLjcyIiB4Mj0iMTM0LjM2IiB5Mj0iNTM4Ljc5NyIvPjxsaW5lIHgxPSI1NjUuNjQiIHkxPSI1OTIuNzIiIHgyPSI1NjUuNjQiIHkyPSI1MzguNzk3Ii8+PHBvbHlsaW5lIHBvaW50cz0iNDgxLjgyMiA0ODEuOTAxIDQ4MS43OTggNDgxLjg3NyA0ODEuNzc1IDQ4MS44NTQgMzUwLjAxNSAzNTAuMDI2IDIxOC4xODUgMjE4LjEyOSIvPjxwb2x5bGluZSBwb2ludHM9IjIxOC4xODUgNDgxLjkwMSAyMTguMjMxIDQ4MS44NTQgMzUwLjAxNSAzNTAuMDI2IDQ4MS44MjIgMjE4LjE1MiIvPjwvZz48L2c+PGcgaWQ9IlByb2dyZXNzIiBmaWxsPSIjZmZmIj48cmVjdCB3aWR0aD0iMjA4IiBoZWlnaHQ9IjEwMCIgZmlsbC1vcGFjaXR5PSIuMDMiIHJ4PSIxNSIgcnk9IjE1IiBzdHJva2U9IiNmZmYiIHN0cm9rZS1vcGFjaXR5PSIuMSIgc3Ryb2tlLXdpZHRoPSI0Ii8+PHRleHQgeD0iMjAiIHk9IjM0IiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjJweCI+UHJvZ3Jlc3M8L3RleHQ+PHRleHQgeD0iMjAiIHk9IjcyIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjZweCI+MjIuOTglPC90ZXh0PjxnIGZpbGw9Im5vbmUiPjxjaXJjbGUgY3g9IjE2NiIgY3k9IjUwIiByPSIyMiIgc3Ryb2tlPSJoc2woMjMwLDIxJSwxMSUpIiBzdHJva2Utd2lkdGg9IjEwIi8+PGNpcmNsZSBjeD0iMTY2IiBjeT0iNTAiIHBhdGhMZW5ndGg9IjEwMDAwIiByPSIyMiIgc3Ryb2tlPSJoc2woNjMsOTUlLDU3JSkiIHN0cm9rZS1kYXNoYXJyYXk9IjEwMDAwIiBzdHJva2UtZGFzaG9mZnNldD0iNzcwMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjUiIHRyYW5zZm9ybT0icm90YXRlKC05MCkiIHRyYW5zZm9ybS1vcmlnaW49IjE2NiA1MCIvPjwvZz48L2c+PGcgaWQ9IlN0YXR1cyIgZmlsbD0iI2ZmZiI+PHJlY3Qgd2lkdGg9IjE4NCIgaGVpZ2h0PSIxMDAiIGZpbGwtb3BhY2l0eT0iLjAzIiByeD0iMTUiIHJ5PSIxNSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZS13aWR0aD0iNCIvPjx0ZXh0IHg9IjIwIiB5PSIzNCIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjIycHgiPlN0YXR1czwvdGV4dD48dGV4dCB4PSIyMCIgeT0iNzIiIGZvbnQtZmFtaWx5PSInQ291cmllciBOZXcnLEFyaWFsLG1vbm9zcGFjZSIgZm9udC1zaXplPSIyNnB4Ij5TdHJlYW1pbmc8L3RleHQ+PC9nPjxnIGlkPSJBbW91bnQiIGZpbGw9IiNmZmYiPjxyZWN0IHdpZHRoPSIxMjAiIGhlaWdodD0iMTAwIiBmaWxsLW9wYWNpdHk9Ii4wMyIgcng9IjE1IiByeT0iMTUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLW9wYWNpdHk9Ii4xIiBzdHJva2Utd2lkdGg9IjQiLz48dGV4dCB4PSIyMCIgeT0iMzQiIGZvbnQtZmFtaWx5PSInQ291cmllciBOZXcnLEFyaWFsLG1vbm9zcGFjZSIgZm9udC1zaXplPSIyMnB4Ij5BbW91bnQ8L3RleHQ+PHRleHQgeD0iMjAiIHk9IjcyIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjZweCI+JiM4ODA1OyAxMEs8L3RleHQ+PC9nPjxnIGlkPSJEdXJhdGlvbiIgZmlsbD0iI2ZmZiI+PHJlY3Qgd2lkdGg9IjE1MiIgaGVpZ2h0PSIxMDAiIGZpbGwtb3BhY2l0eT0iLjAzIiByeD0iMTUiIHJ5PSIxNSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZS13aWR0aD0iNCIvPjx0ZXh0IHg9IjIwIiB5PSIzNCIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjIycHgiPkR1cmF0aW9uPC90ZXh0Pjx0ZXh0IHg9IjIwIiB5PSI3MiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjI2cHgiPiZsdDsgMSBEYXk8L3RleHQ+PC9nPjwvZGVmcz48dGV4dCB0ZXh0LXJlbmRlcmluZz0ib3B0aW1pemVTcGVlZCI+PHRleHRQYXRoIHN0YXJ0T2Zmc2V0PSItMTAwJSIgaHJlZj0iI0Zsb2F0aW5nVGV4dCIgZmlsbD0iI2ZmZiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmaWxsLW9wYWNpdHk9Ii44IiBmb250LXNpemU9IjI2cHgiPjxhbmltYXRlIGFkZGl0aXZlPSJzdW0iIGF0dHJpYnV0ZU5hbWU9InN0YXJ0T2Zmc2V0IiBiZWdpbj0iMHMiIGR1cj0iNTBzIiBmcm9tPSIwJSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHRvPSIxMDAlIi8+MHg5MjNiNWFiMzcxNGZkMzQzMzE2YWY1YTU0MzQ1ODJmZDE2NzIyNTIzIOKAoiBTYWJsaWVyIExvY2t1cDwvdGV4dFBhdGg+PHRleHRQYXRoIHN0YXJ0T2Zmc2V0PSIwJSIgaHJlZj0iI0Zsb2F0aW5nVGV4dCIgZmlsbD0iI2ZmZiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmaWxsLW9wYWNpdHk9Ii44IiBmb250LXNpemU9IjI2cHgiPjxhbmltYXRlIGFkZGl0aXZlPSJzdW0iIGF0dHJpYnV0ZU5hbWU9InN0YXJ0T2Zmc2V0IiBiZWdpbj0iMHMiIGR1cj0iNTBzIiBmcm9tPSIwJSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHRvPSIxMDAlIi8+MHg5MjNiNWFiMzcxNGZkMzQzMzE2YWY1YTU0MzQ1ODJmZDE2NzIyNTIzIOKAoiBTYWJsaWVyIExvY2t1cDwvdGV4dFBhdGg+PHRleHRQYXRoIHN0YXJ0T2Zmc2V0PSItNTAlIiBocmVmPSIjRmxvYXRpbmdUZXh0IiBmaWxsPSIjZmZmIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZpbGwtb3BhY2l0eT0iLjgiIGZvbnQtc2l6ZT0iMjZweCI+PGFuaW1hdGUgYWRkaXRpdmU9InN1bSIgYXR0cmlidXRlTmFtZT0ic3RhcnRPZmZzZXQiIGJlZ2luPSIwcyIgZHVyPSI1MHMiIGZyb209IjAlIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgdG89IjEwMCUiLz4weGY2Mjg0OWY5YTBiNWJmMjkxM2IzOTYwOThmN2M3MDE5YjUxYTgyMGEg4oCiIERBSTwvdGV4dFBhdGg+PHRleHRQYXRoIHN0YXJ0T2Zmc2V0PSI1MCUiIGhyZWY9IiNGbG9hdGluZ1RleHQiIGZpbGw9IiNmZmYiIGZvbnQtZmFtaWx5PSInQ291cmllciBOZXcnLEFyaWFsLG1vbm9zcGFjZSIgZmlsbC1vcGFjaXR5PSIuOCIgZm9udC1zaXplPSIyNnB4Ij48YW5pbWF0ZSBhZGRpdGl2ZT0ic3VtIiBhdHRyaWJ1dGVOYW1lPSJzdGFydE9mZnNldCIgYmVnaW49IjBzIiBkdXI9IjUwcyIgZnJvbT0iMCUiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB0bz0iMTAwJSIvPjB4ZjYyODQ5ZjlhMGI1YmYyOTEzYjM5NjA5OGY3YzcwMTliNTFhODIwYSDigKIgREFJPC90ZXh0UGF0aD48L3RleHQ+PHVzZSBocmVmPSIjR2xvdyIgZmlsbC1vcGFjaXR5PSIuOSIvPjx1c2UgaHJlZj0iI0dsb3ciIHg9IjEwMDAiIHk9IjEwMDAiIGZpbGwtb3BhY2l0eT0iLjkiLz48dXNlIGhyZWY9IiNMb2dvIiB4PSIxNzAiIHk9IjE3MCIgdHJhbnNmb3JtPSJzY2FsZSguNikiLz48dXNlIGhyZWY9IiNIb3VyZ2xhc3MiIHg9IjE1MCIgeT0iOTAiIHRyYW5zZm9ybT0icm90YXRlKDEwKSIgdHJhbnNmb3JtLW9yaWdpbj0iNTAwIDUwMCIvPjx1c2UgaHJlZj0iI1Byb2dyZXNzIiB4PSIxNDQiIHk9Ijc5MCIvPjx1c2UgaHJlZj0iI1N0YXR1cyIgeD0iMzY4IiB5PSI3OTAiLz48dXNlIGhyZWY9IiNBbW91bnQiIHg9IjU2OCIgeT0iNzkwIi8+PHVzZSBocmVmPSIjRHVyYXRpb24iIHg9IjcwNCIgeT0iNzkwIi8+PC9zdmc+"}'; assertEq(actualDecodedTokenURI, expectedDecodedTokenURI, "decoded token URI"); } @@ -52,7 +52,7 @@ contract TokenURI_Lockup_Integration_Concrete_Test is Integration_Test { string memory actualTokenURI = lockup.tokenURI(defaultStreamId); string memory expectedTokenURI = - "data:application/json;base64,eyJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoiVG9rZW4iLCJ2YWx1ZSI6IkVSQzIwIn0seyJ0cmFpdF90eXBlIjoiU2VuZGVyIiwidmFsdWUiOiIweDYzMzJlN2IxZGViMWYxYTBiNzdiMmJiMThiMTQ0MzMwYzcyOTFiY2EifSx7InRyYWl0X3R5cGUiOiJTdGF0dXMiLCJ2YWx1ZSI6IlN0cmVhbWluZyJ9XSwiZGVzY3JpcHRpb24iOiJUaGlzIE5GVCByZXByZXNlbnRzIGEgcGF5bWVudCBzdHJlYW0gaW4gYSBTYWJsaWVyIExvY2t1cCBTYWJsaWVyIExvY2t1cCBjb250cmFjdC4gVGhlIG93bmVyIG9mIHRoaXMgTkZUIGNhbiB3aXRoZHJhdyB0aGUgc3RyZWFtZWQgdG9rZW5zLCB3aGljaCBhcmUgZGVub21pbmF0ZWQgaW4gRVJDMjAuXG5cbi0gU3RyZWFtIElEOiAxXG4tIFNhYmxpZXIgTG9ja3VwIEFkZHJlc3M6IDB4OTIzYjVhYjM3MTRmZDM0MzMxNmFmNWE1NDM0NTgyZmQxNjcyMjUyM1xuLSBFUkMyMCBBZGRyZXNzOiAweGY2Mjg0OWY5YTBiNWJmMjkxM2IzOTYwOThmN2M3MDE5YjUxYTgyMGFcblxu4pqg77iPIFdBUk5JTkc6IFRyYW5zZmVycmluZyB0aGUgTkZUIG1ha2VzIHRoZSBuZXcgb3duZXIgdGhlIHJlY2lwaWVudCBvZiB0aGUgc3RyZWFtLiBUaGUgZnVuZHMgYXJlIG5vdCBhdXRvbWF0aWNhbGx5IHdpdGhkcmF3biBmb3IgdGhlIHByZXZpb3VzIHJlY2lwaWVudC4iLCJleHRlcm5hbF91cmwiOiJodHRwczovL3NhYmxpZXIuY29tIiwibmFtZSI6IlNhYmxpZXIgU2FibGllciBMb2NrdXAgIzEiLCJpbWFnZSI6ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJeE1EQXdJaUJvWldsbmFIUTlJakV3TURBaUlIWnBaWGRDYjNnOUlqQWdNQ0F4TURBd0lERXdNREFpUGp4eVpXTjBJSGRwWkhSb1BTSXhNREFsSWlCb1pXbG5hSFE5SWpFd01DVWlJR1pwYkhSbGNqMGlkWEpzS0NOT2IybHpaU2tpTHo0OGNtVmpkQ0I0UFNJM01DSWdlVDBpTnpBaUlIZHBaSFJvUFNJNE5qQWlJR2hsYVdkb2REMGlPRFl3SWlCbWFXeHNQU0lqWm1abUlpQm1hV3hzTFc5d1lXTnBkSGs5SWk0d015SWdjbmc5SWpRMUlpQnllVDBpTkRVaUlITjBjbTlyWlQwaUkyWm1aaUlnYzNSeWIydGxMVzl3WVdOcGRIazlJaTR4SWlCemRISnZhMlV0ZDJsa2RHZzlJalFpTHo0OFpHVm1jejQ4WTJseVkyeGxJR2xrUFNKSGJHOTNJaUJ5UFNJMU1EQWlJR1pwYkd3OUluVnliQ2dqVW1Ga2FXRnNSMnh2ZHlraUx6NDhabWxzZEdWeUlHbGtQU0pPYjJselpTSStQR1psUm14dmIyUWdlRDBpTUNJZ2VUMGlNQ0lnZDJsa2RHZzlJakV3TUNVaUlHaGxhV2RvZEQwaU1UQXdKU0lnWm14dmIyUXRZMjlzYjNJOUltaHpiQ2d5TXpBc01qRWxMREV4SlNraUlHWnNiMjlrTFc5d1lXTnBkSGs5SWpFaUlISmxjM1ZzZEQwaVpteHZiMlJHYVd4c0lpOCtQR1psVkhWeVluVnNaVzVqWlNCaVlYTmxSbkpsY1hWbGJtTjVQU0l1TkNJZ2JuVnRUMk4wWVhabGN6MGlNeUlnY21WemRXeDBQU0pPYjJselpTSWdkSGx3WlQwaVpuSmhZM1JoYkU1dmFYTmxJaTgrUEdabFFteGxibVFnYVc0OUlrNXZhWE5sSWlCcGJqSTlJbVpzYjI5a1JtbHNiQ0lnYlc5a1pUMGljMjltZEMxc2FXZG9kQ0l2UGp3dlptbHNkR1Z5UGp4d1lYUm9JR2xrUFNKTWIyZHZJaUJtYVd4c1BTSWpabVptSWlCbWFXeHNMVzl3WVdOcGRIazlJaTR4SWlCa1BTSnRNVE16TGpVMU9Td3hNalF1TURNMFl5MHVNREV6TERJdU5ERXlMVEV1TURVNUxEUXVPRFE0TFRJdU9USXpMRFl1TkRBeUxUSXVOVFU0TERFdU9ERTVMVFV1TVRZNExETXVORE01TFRjdU9EZzRMRFF1T1RrMkxURTBMalEwTERndU1qWXlMVE14TGpBME55d3hNaTQxTmpVdE5EY3VOamMwTERFeUxqVTJPUzA0TGpnMU9DNHdNell0TVRjdU9ETTRMVEV1TWpjeUxUSTJMak15T0MwekxqWTJNeTA1TGpnd05pMHlMamMyTmkweE9TNHdPRGN0Tnk0eE1UTXRNamN1TlRZeUxURXlMamMzT0MweE15NDROREl0T0M0d01qVXNPUzQwTmpndE1qZ3VOakEyTERFMkxqRTFNeTB6TlM0eU5qVm9NR015TGpBek5TMHhMamd6T0N3MExqSTFNaTB6TGpVME5pdzJMalEyTXkwMUxqSXlOR2d3WXpZdU5ESTVMVFV1TmpVMUxERTJMakl4T0MweUxqZ3pOU3d5TUM0ek5UZ3NOQzR4Tnl3MExqRTBNeXcxTGpBMU55dzRMamd4Tml3NUxqWTBPU3d4TXk0NU1pd3hNeTQzTXpSb0xqQXpOMk0xTGpjek5pdzJMalEyTVN3eE5TNHpOVGN0TWk0eU5UTXNPUzR6T0MwNExqUTRMREFzTUMwekxqVXhOUzB6TGpVeE5TMHpMalV4TlMwekxqVXhOUzB4TVM0ME9TMHhNUzQwTnpndE5USXVOalUyTFRVeUxqWTJOQzAyTkM0NE16Y3ROalF1T0RNM2JDNHdORGt0TGpBek4yTXRNUzQzTWpVdE1TNDJNRFl0TWk0M01Ua3RNeTQ0TkRjdE1pNDNOVEV0Tmk0eU1EUm9NR010TGpBME5pMHlMak0zTlN3eExqQTJNaTAwTGpVNE1pd3lMamN5TmkwMkxqSXlPV2d3YkM0eE9EVXRMakUwT0dnd1l5NHdPVGt0TGpBMk1pd3VNakl5TFM0eE5EZ3NMak0zTFM0eU5UbG9NR015TGpBMkxURXVNell5TERNdU9UVXhMVEl1TmpJeExEWXVNRFEwTFRNdU9EUXlRelUzTGpjMk15MHpMalEzTXl3NU55NDNOaTB5TGpNME1Td3hNamd1TmpNM0xERTRMak16TW1NeE5pNDJOekVzT1M0NU5EWXRNall1TXpRMExEVTBMamd4TXkwek9DNDJOVEVzTkRBdU1UazVMVFl1TWprNUxUWXVNRGsyTFRFNExqQTJNeTB4Tnk0M05ETXRNVGt1TmpZNExURTRMamd4TVMwMkxqQXhOaTAwTGpBME55MHhNeTR3TmpFc05DNDNOell0Tnk0M05USXNPUzQzTlRGc05qZ3VNalUwTERZNExqTTNNV014TGpjeU5Dd3hMall3TVN3eUxqY3hOQ3d6TGpnMExESXVOek00TERZdU1Ua3lXaUl2UGp4d1lYUm9JR2xrUFNKR2JHOWhkR2x1WjFSbGVIUWlJR1pwYkd3OUltNXZibVVpSUdROUlrMHhNalVnTkRWb056VXdjemd3SURBZ09EQWdPREIyTnpVd2N6QWdPREFnTFRnd0lEZ3dhQzAzTlRCekxUZ3dJREFnTFRnd0lDMDRNSFl0TnpVd2N6QWdMVGd3SURnd0lDMDRNQ0l2UGp4eVlXUnBZV3hIY21Ga2FXVnVkQ0JwWkQwaVVtRmthV0ZzUjJ4dmR5SStQSE4wYjNBZ2IyWm1jMlYwUFNJd0pTSWdjM1J2Y0MxamIyeHZjajBpYUhOc0tEWXpMRGsxSlN3MU55VXBJaUJ6ZEc5d0xXOXdZV05wZEhrOUlpNDJJaTgrUEhOMGIzQWdiMlptYzJWMFBTSXhNREFsSWlCemRHOXdMV052Ykc5eVBTSm9jMndvTWpNd0xESXhKU3d4TVNVcElpQnpkRzl3TFc5d1lXTnBkSGs5SWpBaUx6NDhMM0poWkdsaGJFZHlZV1JwWlc1MFBqeHNhVzVsWVhKSGNtRmthV1Z1ZENCcFpEMGlVMkZ1WkZSdmNDSWdlREU5SWpBbElpQjVNVDBpTUNVaVBqeHpkRzl3SUc5bVpuTmxkRDBpTUNVaUlITjBiM0F0WTI5c2IzSTlJbWh6YkNnMk15dzVOU1VzTlRjbEtTSXZQanh6ZEc5d0lHOW1abk5sZEQwaU1UQXdKU0lnYzNSdmNDMWpiMnh2Y2owaWFITnNLREl6TUN3eU1TVXNNVEVsS1NJdlBqd3ZiR2x1WldGeVIzSmhaR2xsYm5RK1BHeHBibVZoY2tkeVlXUnBaVzUwSUdsa1BTSlRZVzVrUW05MGRHOXRJaUI0TVQwaU1UQXdKU0lnZVRFOUlqRXdNQ1VpUGp4emRHOXdJRzltWm5ObGREMGlNVEFsSWlCemRHOXdMV052Ykc5eVBTSm9jMndvTWpNd0xESXhKU3d4TVNVcElpOCtQSE4wYjNBZ2IyWm1jMlYwUFNJeE1EQWxJaUJ6ZEc5d0xXTnZiRzl5UFNKb2Myd29Oak1zT1RVbExEVTNKU2tpTHo0OFlXNXBiV0YwWlNCaGRIUnlhV0oxZEdWT1lXMWxQU0o0TVNJZ1pIVnlQU0kyY3lJZ2NtVndaV0YwUTI5MWJuUTlJbWx1WkdWbWFXNXBkR1VpSUhaaGJIVmxjejBpTXpBbE96WXdKVHN4TWpBbE96WXdKVHN6TUNVN0lpOCtQQzlzYVc1bFlYSkhjbUZrYVdWdWRENDhiR2x1WldGeVIzSmhaR2xsYm5RZ2FXUTlJa2h2ZFhKbmJHRnpjMU4wY205clpTSWdaM0poWkdsbGJuUlVjbUZ1YzJadmNtMDlJbkp2ZEdGMFpTZzVNQ2tpSUdkeVlXUnBaVzUwVlc1cGRITTlJblZ6WlhKVGNHRmpaVTl1VlhObElqNDhjM1J2Y0NCdlptWnpaWFE5SWpVd0pTSWdjM1J2Y0MxamIyeHZjajBpYUhOc0tEWXpMRGsxSlN3MU55VXBJaTgrUEhOMGIzQWdiMlptYzJWMFBTSTRNQ1VpSUhOMGIzQXRZMjlzYjNJOUltaHpiQ2d5TXpBc01qRWxMREV4SlNraUx6NDhMMnhwYm1WaGNrZHlZV1JwWlc1MFBqeG5JR2xrUFNKSWIzVnlaMnhoYzNNaVBqeHdZWFJvSUdROUlrMGdOVEFzTXpZd0lHRWdNekF3TERNd01DQXdJREVzTVNBMk1EQXNNQ0JoSURNd01Dd3pNREFnTUNBeExERWdMVFl3TUN3d0lpQm1hV3hzUFNJalptWm1JaUJtYVd4c0xXOXdZV05wZEhrOUlpNHdNaUlnYzNSeWIydGxQU0oxY213b0kwaHZkWEpuYkdGemMxTjBjbTlyWlNraUlITjBjbTlyWlMxM2FXUjBhRDBpTkNJdlBqeHdZWFJvSUdROUltMDFOallzTVRZeExqSXdNWFl0TlRNdU9USTBZekF0TVRrdU16Z3lMVEl5TGpVeE15MHpOeTQxTmpNdE5qTXVNems0TFRVeExqRTVPQzAwTUM0M05UWXRNVE11TlRreUxUazBMamswTmkweU1TNHdOemt0TVRVeUxqVTROeTB5TVM0d056bHpMVEV4TVM0NE16Z3NOeTQwT0RjdE1UVXlMall3TWl3eU1TNHdOemxqTFRRd0xqZzVNeXd4TXk0Mk16WXROak11TkRFekxETXhMamd4TmkwMk15NDBNVE1zTlRFdU1UazRkalV6TGpreU5HTXdMREUzTGpFNE1Td3hOeTQzTURRc016TXVOREkzTERVd0xqSXlNeXcwTmk0ek9UUjJNamcwTGpnd09XTXRNekl1TlRFNUxERXlMamsyTFRVd0xqSXlNeXd5T1M0eU1EWXROVEF1TWpJekxEUTJMak01TkhZMU15NDVNalJqTUN3eE9TNHpPRElzTWpJdU5USXNNemN1TlRZekxEWXpMalF4TXl3MU1TNHhPVGdzTkRBdU56WXpMREV6TGpVNU1pdzVOQzQ1TlRRc01qRXVNRGM1TERFMU1pNDJNRElzTWpFdU1EYzVjekV4TVM0NE16RXROeTQwT0Rjc01UVXlMalU0TnkweU1TNHdOemxqTkRBdU9EZzJMVEV6TGpZek5pdzJNeTR6T1RndE16RXVPREUyTERZekxqTTVPQzAxTVM0eE9UaDJMVFV6TGpreU5HTXdMVEUzTGpFNU5pMHhOeTQzTURRdE16TXVORE0xTFRVd0xqSXlNeTAwTmk0ME1ERldNakEzTGpZd00yTXpNaTQxTVRrdE1USXVPVFkzTERVd0xqSXlNeTB5T1M0eU1EWXNOVEF1TWpJekxUUTJMalF3TVZwdExUTTBOeTQwTmpJc05UY3VOemt6YkRFek1DNDVOVGtzTVRNeExqQXlOeTB4TXpBdU9UVTVMREV6TVM0d01UTldNakU0TGprNU5GcHRNall5TGpreU5DNHdNakoyTWpZeUxqQXhPR3d0TVRNd0xqa3pOeTB4TXpFdU1EQTJMREV6TUM0NU16Y3RNVE14TGpBeE0xb2lJR1pwYkd3OUlpTXhOakU0TWpJaVBqd3ZjR0YwYUQ0OGNHOXNlV2R2YmlCd2IybHVkSE05SWpNMU1DQXpOVEF1TURJMklEUXhOUzR3TXlBeU9EUXVPVGM0SURJNE5TQXlPRFF1T1RjNElETTFNQ0F6TlRBdU1ESTJJaUJtYVd4c1BTSjFjbXdvSTFOaGJtUkNiM1IwYjIwcElpOCtQSEJoZEdnZ1pEMGliVFF4Tmk0ek5ERXNNamd4TGprM05XTXdMQzQ1TVRRdExqTTFOQ3d4TGpnd09TMHhMakF6TlN3eUxqWTRMVFV1TlRReUxEY3VNRGMyTFRNeUxqWTJNU3d4TWk0ME5TMDJOUzR5T0N3eE1pNDBOUzB6TWk0Mk1qUXNNQzAxT1M0M016Z3ROUzR6TnpRdE5qVXVNamd0TVRJdU5EVXRMalk0TVMwdU9EY3lMVEV1TURNMUxURXVOelkzTFRFdU1ETTFMVEl1Tmpnc01DMHVPVEUwTGpNMU5DMHhMamd3T0N3eExqQXpOUzB5TGpZM05pdzFMalUwTWkwM0xqQTNOaXd6TWk0Mk5UWXRNVEl1TkRVc05qVXVNamd0TVRJdU5EVXNNekl1TmpFNUxEQXNOVGt1TnpNNExEVXVNemMwTERZMUxqSTRMREV5TGpRMUxqWTRNUzQ0Tmpjc01TNHdNelVzTVM0M05qSXNNUzR3TXpVc01pNDJOelphSWlCbWFXeHNQU0oxY213b0kxTmhibVJVYjNBcElpOCtQSEJoZEdnZ1pEMGliVFE0TVM0ME5pdzFNRFF1TVRBeGRqVTRMalEwT1dNdE1pNHpOUzQzTnkwMExqZ3lMREV1TlRFdE55NHpPU3d5TGpJekxUTXdMak1zT0M0MU5DMDNOQzQyTlN3eE15NDVNaTB4TWpRdU1EWXNNVE11T1RJdE5UTXVOaXd3TFRFd01TNHlOQzAyTGpNekxURXpNUzQwTnkweE5pNHhObll0TlRndU5ETTVhREkyTWk0NU1sb2lJR1pwYkd3OUluVnliQ2dqVTJGdVpFSnZkSFJ2YlNraUx6NDhaV3hzYVhCelpTQmplRDBpTXpVd0lpQmplVDBpTlRBMExqRXdNU0lnY25nOUlqRXpNUzQwTmpJaUlISjVQU0l5T0M0eE1EZ2lJR1pwYkd3OUluVnliQ2dqVTJGdVpGUnZjQ2tpTHo0OFp5Qm1hV3hzUFNKdWIyNWxJaUJ6ZEhKdmEyVTlJblZ5YkNnalNHOTFjbWRzWVhOelUzUnliMnRsS1NJZ2MzUnliMnRsTFd4cGJtVmpZWEE5SW5KdmRXNWtJaUJ6ZEhKdmEyVXRiV2wwWlhKc2FXMXBkRDBpTVRBaUlITjBjbTlyWlMxM2FXUjBhRDBpTkNJK1BIQmhkR2dnWkQwaWJUVTJOUzQyTkRFc01UQTNMakk0WXpBc09TNDFNemN0TlM0MU5pd3hPQzQyTWprdE1UVXVOamMyTERJMkxqazNNMmd0TGpBeU0yTXRPUzR5TURRc055NDFPVFl0TWpJdU1UazBMREUwTGpVMk1pMHpPQzR4T1Rjc01qQXVOVGt5TFRNNUxqVXdOQ3d4TkM0NU16WXRPVGN1TXpJMUxESTBMak0xTlMweE5qRXVOek16TERJMExqTTFOUzA1TUM0ME9Dd3dMVEUyTnk0NU5EZ3RNVGd1TlRneUxURTVPUzQ1TlRNdE5EUXVPVFE0YUMwdU1ESXpZeTB4TUM0eE1UVXRPQzR6TkRRdE1UVXVOamMyTFRFM0xqUXpOeTB4TlM0Mk56WXRNall1T1RjekxEQXRNemt1TnpNMUxEazJMalUxTkMwM01TNDVNakVzTWpFMUxqWTFNaTAzTVM0NU1qRnpNakUxTGpZeU9Td3pNaTR4T0RVc01qRTFMall5T1N3M01TNDVNakZhSWk4K1BIQmhkR2dnWkQwaWJURXpOQzR6Tml3eE5qRXVNakF6WXpBc016a3VOek0xTERrMkxqVTFOQ3czTVM0NU1qRXNNakUxTGpZMU1pdzNNUzQ1TWpGek1qRTFMall5T1Mwek1pNHhPRFlzTWpFMUxqWXlPUzAzTVM0NU1qRWlMejQ4YkdsdVpTQjRNVDBpTVRNMExqTTJJaUI1TVQwaU1UWXhMakl3TXlJZ2VESTlJakV6TkM0ek5pSWdlVEk5SWpFd055NHlPQ0l2UGp4c2FXNWxJSGd4UFNJMU5qVXVOalFpSUhreFBTSXhOakV1TWpBeklpQjRNajBpTlRZMUxqWTBJaUI1TWowaU1UQTNMakk0SWk4K1BHeHBibVVnZURFOUlqRTROQzQxT0RRaUlIa3hQU0l5TURZdU9ESXpJaUI0TWowaU1UZzBMalU0TlNJZ2VUSTlJalV6Tnk0MU56a2lMejQ4YkdsdVpTQjRNVDBpTWpFNExqRTRNU0lnZVRFOUlqSXhPQzR4TVRnaUlIZ3lQU0l5TVRndU1UZ3hJaUI1TWowaU5UWXlMalV6TnlJdlBqeHNhVzVsSUhneFBTSTBPREV1T0RFNElpQjVNVDBpTWpFNExqRTBNaUlnZURJOUlqUTRNUzQ0TVRraUlIa3lQU0kxTmpJdU5ESTRJaTgrUEd4cGJtVWdlREU5SWpVeE5TNDBNVFVpSUhreFBTSXlNRGN1TXpVeUlpQjRNajBpTlRFMUxqUXhOaUlnZVRJOUlqVXpOeTQxTnpraUx6NDhjR0YwYUNCa1BTSnRNVGcwTGpVNExEVXpOeTQxT0dNd0xEVXVORFVzTkM0eU55d3hNQzQyTlN3eE1pNHdNeXd4TlM0ME1tZ3VNREpqTlM0MU1Td3pMak01TERFeUxqYzVMRFl1TlRVc01qRXVOVFVzT1M0ME1pd3pNQzR5TVN3NUxqa3NOemd1TURJc01UWXVNamdzTVRNeExqZ3pMREUyTGpJNExEUTVMalF4TERBc09UTXVOell0TlM0ek9Dd3hNalF1TURZdE1UTXVPVElzTWk0M0xTNDNOaXcxTGpJNUxURXVOVFFzTnk0M05TMHlMak0xTERndU56Y3RNaTQ0Tnl3eE5pNHdOUzAyTGpBMExESXhMalUyTFRrdU5ETm9NR00zTGpjMkxUUXVOemNzTVRJdU1EUXRPUzQ1Tnl3eE1pNHdOQzB4TlM0ME1pSXZQanh3WVhSb0lHUTlJbTB4T0RRdU5UZ3lMRFE1TWk0Mk5UWmpMVE14TGpNMU5Dd3hNaTQwT0RVdE5UQXVNakl6TERJNExqVTRMVFV3TGpJeU15dzBOaTR4TkRJc01DdzVMalV6Tml3MUxqVTJOQ3d4T0M0Mk1qY3NNVFV1TmpjM0xESTJMamsyT1dndU1ESXlZemd1TlRBekxEY3VNREExTERJd0xqSXhNeXd4TXk0ME5qTXNNelF1TlRJMExERTVMakUxT1N3NUxqazVPU3d6TGprNU1Td3lNUzR5Tmprc055NDJNRGtzTXpNdU5UazNMREV3TGpjNE9Dd3pOaTQwTlN3NUxqUXdOeXc0TWk0eE9ERXNNVFV1TURBeUxERXpNUzQ0TXpVc01UVXVNREF5Y3prMUxqTTJNeTAxTGpVNU5Td3hNekV1T0RBM0xURTFMakF3TW1NeE1DNDRORGN0TWk0M09Td3lNQzQ0TmpjdE5TNDVNallzTWprdU9USTBMVGt1TXpRNUxERXVNalEwTFM0ME5qY3NNaTQwTnpNdExqazBNaXd6TGpZM015MHhMalF5TkN3eE5DNHpNall0TlM0Mk9UWXNNall1TURNMUxURXlMakUyTVN3ek5DNDFNalF0TVRrdU1UY3phQzR3TWpKak1UQXVNVEUwTFRndU16UXlMREUxTGpZM055MHhOeTQwTXpNc01UVXVOamMzTFRJMkxqazJPU3d3TFRFM0xqVTJNaTB4T0M0NE5qa3RNek11TmpZMUxUVXdMakl5TXkwME5pNHhOU0l2UGp4d1lYUm9JR1E5SW0weE16UXVNellzTlRreUxqY3lZekFzTXprdU56TTFMRGsyTGpVMU5DdzNNUzQ1TWpFc01qRTFMalkxTWl3M01TNDVNakZ6TWpFMUxqWXlPUzB6TWk0eE9EWXNNakUxTGpZeU9TMDNNUzQ1TWpFaUx6NDhiR2x1WlNCNE1UMGlNVE0wTGpNMklpQjVNVDBpTlRreUxqY3lJaUI0TWowaU1UTTBMak0ySWlCNU1qMGlOVE00TGpjNU55SXZQanhzYVc1bElIZ3hQU0kxTmpVdU5qUWlJSGt4UFNJMU9USXVOeklpSUhneVBTSTFOalV1TmpRaUlIa3lQU0kxTXpndU56azNJaTgrUEhCdmJIbHNhVzVsSUhCdmFXNTBjejBpTkRneExqZ3lNaUEwT0RFdU9UQXhJRFE0TVM0M09UZ2dORGd4TGpnM055QTBPREV1TnpjMUlEUTRNUzQ0TlRRZ016VXdMakF4TlNBek5UQXVNREkySURJeE9DNHhPRFVnTWpFNExqRXlPU0l2UGp4d2IyeDViR2x1WlNCd2IybHVkSE05SWpJeE9DNHhPRFVnTkRneExqa3dNU0F5TVRndU1qTXhJRFE0TVM0NE5UUWdNelV3TGpBeE5TQXpOVEF1TURJMklEUTRNUzQ0TWpJZ01qRTRMakUxTWlJdlBqd3ZaejQ4TDJjK1BHY2dhV1E5SWxCeWIyZHlaWE56SWlCbWFXeHNQU0lqWm1abUlqNDhjbVZqZENCM2FXUjBhRDBpTWpBNElpQm9aV2xuYUhROUlqRXdNQ0lnWm1sc2JDMXZjR0ZqYVhSNVBTSXVNRE1pSUhKNFBTSXhOU0lnY25rOUlqRTFJaUJ6ZEhKdmEyVTlJaU5tWm1ZaUlITjBjbTlyWlMxdmNHRmphWFI1UFNJdU1TSWdjM1J5YjJ0bExYZHBaSFJvUFNJMElpOCtQSFJsZUhRZ2VEMGlNakFpSUhrOUlqTTBJaUJtYjI1MExXWmhiV2xzZVQwaUowTnZkWEpwWlhJZ1RtVjNKeXhCY21saGJDeHRiMjV2YzNCaFkyVWlJR1p2Ym5RdGMybDZaVDBpTWpKd2VDSStVSEp2WjNKbGMzTThMM1JsZUhRK1BIUmxlSFFnZUQwaU1qQWlJSGs5SWpjeUlpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5eEJjbWxoYkN4dGIyNXZjM0JoWTJVaUlHWnZiblF0YzJsNlpUMGlNalp3ZUNJK01qSXVPVGdsUEM5MFpYaDBQanhuSUdacGJHdzlJbTV2Ym1VaVBqeGphWEpqYkdVZ1kzZzlJakUyTmlJZ1kzazlJalV3SWlCeVBTSXlNaUlnYzNSeWIydGxQU0pvYzJ3b01qTXdMREl4SlN3eE1TVXBJaUJ6ZEhKdmEyVXRkMmxrZEdnOUlqRXdJaTgrUEdOcGNtTnNaU0JqZUQwaU1UWTJJaUJqZVQwaU5UQWlJSEJoZEdoTVpXNW5kR2c5SWpFd01EQXdJaUJ5UFNJeU1pSWdjM1J5YjJ0bFBTSm9jMndvTmpNc09UVWxMRFUzSlNraUlITjBjbTlyWlMxa1lYTm9ZWEp5WVhrOUlqRXdNREF3SWlCemRISnZhMlV0WkdGemFHOW1abk5sZEQwaU56Y3dNaUlnYzNSeWIydGxMV3hwYm1WallYQTlJbkp2ZFc1a0lpQnpkSEp2YTJVdGQybGtkR2c5SWpVaUlIUnlZVzV6Wm05eWJUMGljbTkwWVhSbEtDMDVNQ2tpSUhSeVlXNXpabTl5YlMxdmNtbG5hVzQ5SWpFMk5pQTFNQ0l2UGp3dlp6NDhMMmMrUEdjZ2FXUTlJbE4wWVhSMWN5SWdabWxzYkQwaUkyWm1aaUkrUEhKbFkzUWdkMmxrZEdnOUlqRTROQ0lnYUdWcFoyaDBQU0l4TURBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpBeklpQnllRDBpTVRVaUlISjVQU0l4TlNJZ2MzUnliMnRsUFNJalptWm1JaUJ6ZEhKdmEyVXRiM0JoWTJsMGVUMGlMakVpSUhOMGNtOXJaUzEzYVdSMGFEMGlOQ0l2UGp4MFpYaDBJSGc5SWpJd0lpQjVQU0l6TkNJZ1ptOXVkQzFtWVcxcGJIazlJaWREYjNWeWFXVnlJRTVsZHljc1FYSnBZV3dzYlc5dWIzTndZV05sSWlCbWIyNTBMWE5wZW1VOUlqSXljSGdpUGxOMFlYUjFjend2ZEdWNGRENDhkR1Y0ZENCNFBTSXlNQ0lnZVQwaU56SWlJR1p2Ym5RdFptRnRhV3g1UFNJblEyOTFjbWxsY2lCT1pYY25MRUZ5YVdGc0xHMXZibTl6Y0dGalpTSWdabTl1ZEMxemFYcGxQU0l5Tm5CNElqNVRkSEpsWVcxcGJtYzhMM1JsZUhRK1BDOW5QanhuSUdsa1BTSkJiVzkxYm5RaUlHWnBiR3c5SWlObVptWWlQanh5WldOMElIZHBaSFJvUFNJeE1qQWlJR2hsYVdkb2REMGlNVEF3SWlCbWFXeHNMVzl3WVdOcGRIazlJaTR3TXlJZ2NuZzlJakUxSWlCeWVUMGlNVFVpSUhOMGNtOXJaVDBpSTJabVppSWdjM1J5YjJ0bExXOXdZV05wZEhrOUlpNHhJaUJ6ZEhKdmEyVXRkMmxrZEdnOUlqUWlMejQ4ZEdWNGRDQjRQU0l5TUNJZ2VUMGlNelFpSUdadmJuUXRabUZ0YVd4NVBTSW5RMjkxY21sbGNpQk9aWGNuTEVGeWFXRnNMRzF2Ym05emNHRmpaU0lnWm05dWRDMXphWHBsUFNJeU1uQjRJajVCYlc5MWJuUThMM1JsZUhRK1BIUmxlSFFnZUQwaU1qQWlJSGs5SWpjeUlpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5eEJjbWxoYkN4dGIyNXZjM0JoWTJVaUlHWnZiblF0YzJsNlpUMGlNalp3ZUNJK0ppTTRPREExT3lBeE1FczhMM1JsZUhRK1BDOW5QanhuSUdsa1BTSkVkWEpoZEdsdmJpSWdabWxzYkQwaUkyWm1aaUkrUEhKbFkzUWdkMmxrZEdnOUlqRTFNaUlnYUdWcFoyaDBQU0l4TURBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpBeklpQnllRDBpTVRVaUlISjVQU0l4TlNJZ2MzUnliMnRsUFNJalptWm1JaUJ6ZEhKdmEyVXRiM0JoWTJsMGVUMGlMakVpSUhOMGNtOXJaUzEzYVdSMGFEMGlOQ0l2UGp4MFpYaDBJSGc5SWpJd0lpQjVQU0l6TkNJZ1ptOXVkQzFtWVcxcGJIazlJaWREYjNWeWFXVnlJRTVsZHljc1FYSnBZV3dzYlc5dWIzTndZV05sSWlCbWIyNTBMWE5wZW1VOUlqSXljSGdpUGtSMWNtRjBhVzl1UEM5MFpYaDBQangwWlhoMElIZzlJakl3SWlCNVBTSTNNaUlnWm05dWRDMW1ZVzFwYkhrOUlpZERiM1Z5YVdWeUlFNWxkeWNzUVhKcFlXd3NiVzl1YjNOd1lXTmxJaUJtYjI1MExYTnBlbVU5SWpJMmNIZ2lQaVpzZERzZ01TQkVZWGs4TDNSbGVIUStQQzluUGp3dlpHVm1jejQ4ZEdWNGRDQjBaWGgwTFhKbGJtUmxjbWx1WnowaWIzQjBhVzFwZW1WVGNHVmxaQ0krUEhSbGVIUlFZWFJvSUhOMFlYSjBUMlptYzJWMFBTSXRNVEF3SlNJZ2FISmxaajBpSTBac2IyRjBhVzVuVkdWNGRDSWdabWxzYkQwaUkyWm1aaUlnWm05dWRDMW1ZVzFwYkhrOUlpZERiM1Z5YVdWeUlFNWxkeWNzUVhKcFlXd3NiVzl1YjNOd1lXTmxJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDRJaUJtYjI1MExYTnBlbVU5SWpJMmNIZ2lQanhoYm1sdFlYUmxJR0ZrWkdsMGFYWmxQU0p6ZFcwaUlHRjBkSEpwWW5WMFpVNWhiV1U5SW5OMFlYSjBUMlptYzJWMElpQmlaV2RwYmowaU1ITWlJR1IxY2owaU5UQnpJaUJtY205dFBTSXdKU0lnY21Wd1pXRjBRMjkxYm5ROUltbHVaR1ZtYVc1cGRHVWlJSFJ2UFNJeE1EQWxJaTgrTUhnNU1qTmlOV0ZpTXpjeE5HWmtNelF6TXpFMllXWTFZVFUwTXpRMU9ESm1aREUyTnpJeU5USXpJT0tBb2lCVFlXSnNhV1Z5SUZOaFlteHBaWElnVEc5amEzVndQQzkwWlhoMFVHRjBhRDQ4ZEdWNGRGQmhkR2dnYzNSaGNuUlBabVp6WlhROUlqQWxJaUJvY21WbVBTSWpSbXh2WVhScGJtZFVaWGgwSWlCbWFXeHNQU0lqWm1abUlpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5eEJjbWxoYkN4dGIyNXZjM0JoWTJVaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpnaUlHWnZiblF0YzJsNlpUMGlNalp3ZUNJK1BHRnVhVzFoZEdVZ1lXUmthWFJwZG1VOUluTjFiU0lnWVhSMGNtbGlkWFJsVG1GdFpUMGljM1JoY25SUFptWnpaWFFpSUdKbFoybHVQU0l3Y3lJZ1pIVnlQU0kxTUhNaUlHWnliMjA5SWpBbElpQnlaWEJsWVhSRGIzVnVkRDBpYVc1a1pXWnBibWwwWlNJZ2RHODlJakV3TUNVaUx6NHdlRGt5TTJJMVlXSXpOekUwWm1Rek5ETXpNVFpoWmpWaE5UUXpORFU0TW1aa01UWTNNakkxTWpNZzRvQ2lJRk5oWW14cFpYSWdVMkZpYkdsbGNpQk1iMk5yZFhBOEwzUmxlSFJRWVhSb1BqeDBaWGgwVUdGMGFDQnpkR0Z5ZEU5bVpuTmxkRDBpTFRVd0pTSWdhSEpsWmowaUkwWnNiMkYwYVc1blZHVjRkQ0lnWm1sc2JEMGlJMlptWmlJZ1ptOXVkQzFtWVcxcGJIazlJaWREYjNWeWFXVnlJRTVsZHljc1FYSnBZV3dzYlc5dWIzTndZV05sSWlCbWFXeHNMVzl3WVdOcGRIazlJaTQ0SWlCbWIyNTBMWE5wZW1VOUlqSTJjSGdpUGp4aGJtbHRZWFJsSUdGa1pHbDBhWFpsUFNKemRXMGlJR0YwZEhKcFluVjBaVTVoYldVOUluTjBZWEowVDJabWMyVjBJaUJpWldkcGJqMGlNSE1pSUdSMWNqMGlOVEJ6SWlCbWNtOXRQU0l3SlNJZ2NtVndaV0YwUTI5MWJuUTlJbWx1WkdWbWFXNXBkR1VpSUhSdlBTSXhNREFsSWk4K01IaG1Oakk0TkRsbU9XRXdZalZpWmpJNU1UTmlNemsyTURrNFpqZGpOekF4T1dJMU1XRTRNakJoSU9LQW9pQkZVa015TUR3dmRHVjRkRkJoZEdnK1BIUmxlSFJRWVhSb0lITjBZWEowVDJabWMyVjBQU0kxTUNVaUlHaHlaV1k5SWlOR2JHOWhkR2x1WjFSbGVIUWlJR1pwYkd3OUlpTm1abVlpSUdadmJuUXRabUZ0YVd4NVBTSW5RMjkxY21sbGNpQk9aWGNuTEVGeWFXRnNMRzF2Ym05emNHRmpaU0lnWm1sc2JDMXZjR0ZqYVhSNVBTSXVPQ0lnWm05dWRDMXphWHBsUFNJeU5uQjRJajQ4WVc1cGJXRjBaU0JoWkdScGRHbDJaVDBpYzNWdElpQmhkSFJ5YVdKMWRHVk9ZVzFsUFNKemRHRnlkRTltWm5ObGRDSWdZbVZuYVc0OUlqQnpJaUJrZFhJOUlqVXdjeUlnWm5KdmJUMGlNQ1VpSUhKbGNHVmhkRU52ZFc1MFBTSnBibVJsWm1sdWFYUmxJaUIwYnowaU1UQXdKU0l2UGpCNFpqWXlPRFE1WmpsaE1HSTFZbVl5T1RFellqTTVOakE1T0dZM1l6Y3dNVGxpTlRGaE9ESXdZU0RpZ0tJZ1JWSkRNakE4TDNSbGVIUlFZWFJvUGp3dmRHVjRkRDQ4ZFhObElHaHlaV1k5SWlOSGJHOTNJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDVJaTgrUEhWelpTQm9jbVZtUFNJalIyeHZkeUlnZUQwaU1UQXdNQ0lnZVQwaU1UQXdNQ0lnWm1sc2JDMXZjR0ZqYVhSNVBTSXVPU0l2UGp4MWMyVWdhSEpsWmowaUkweHZaMjhpSUhnOUlqRTNNQ0lnZVQwaU1UY3dJaUIwY21GdWMyWnZjbTA5SW5OallXeGxLQzQyS1NJdlBqeDFjMlVnYUhKbFpqMGlJMGh2ZFhKbmJHRnpjeUlnZUQwaU1UVXdJaUI1UFNJNU1DSWdkSEpoYm5ObWIzSnRQU0p5YjNSaGRHVW9NVEFwSWlCMGNtRnVjMlp2Y20wdGIzSnBaMmx1UFNJMU1EQWdOVEF3SWk4K1BIVnpaU0JvY21WbVBTSWpVSEp2WjNKbGMzTWlJSGc5SWpFME5DSWdlVDBpTnprd0lpOCtQSFZ6WlNCb2NtVm1QU0lqVTNSaGRIVnpJaUI0UFNJek5qZ2lJSGs5SWpjNU1DSXZQangxYzJVZ2FISmxaajBpSTBGdGIzVnVkQ0lnZUQwaU5UWTRJaUI1UFNJM09UQWlMejQ4ZFhObElHaHlaV1k5SWlORWRYSmhkR2x2YmlJZ2VEMGlOekEwSWlCNVBTSTNPVEFpTHo0OEwzTjJaejQ9In0="; + "data:application/json;base64,eyJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoiVG9rZW4iLCJ2YWx1ZSI6IkRBSSJ9LHsidHJhaXRfdHlwZSI6IlNlbmRlciIsInZhbHVlIjoiMHg2MzMyZTdiMWRlYjFmMWEwYjc3YjJiYjE4YjE0NDMzMGM3MjkxYmNhIn0seyJ0cmFpdF90eXBlIjoiU3RhdHVzIiwidmFsdWUiOiJTdHJlYW1pbmcifV0sImRlc2NyaXB0aW9uIjoiVGhpcyBORlQgcmVwcmVzZW50cyBhIHN0cmVhbSBpbiBTYWJsaWVyIExvY2t1cCBjb250cmFjdC4gVGhlIG93bmVyIG9mIHRoaXMgTkZUIGNhbiB3aXRoZHJhdyB0aGUgc3RyZWFtZWQgdG9rZW5zLCB3aGljaCBhcmUgZGVub21pbmF0ZWQgaW4gREFJLlxuXG4tIFN0cmVhbSBJRDogMVxuLSBTYWJsaWVyIExvY2t1cCBBZGRyZXNzOiAweDkyM2I1YWIzNzE0ZmQzNDMzMTZhZjVhNTQzNDU4MmZkMTY3MjI1MjNcbi0gREFJIEFkZHJlc3M6IDB4ZjYyODQ5ZjlhMGI1YmYyOTEzYjM5NjA5OGY3YzcwMTliNTFhODIwYVxuXG7imqDvuI8gV0FSTklORzogVHJhbnNmZXJyaW5nIHRoZSBORlQgbWFrZXMgdGhlIG5ldyBvd25lciB0aGUgcmVjaXBpZW50IG9mIHRoZSBzdHJlYW0uIFRoZSBmdW5kcyBhcmUgbm90IGF1dG9tYXRpY2FsbHkgd2l0aGRyYXduIGZvciB0aGUgcHJldmlvdXMgcmVjaXBpZW50LiIsImV4dGVybmFsX3VybCI6Imh0dHBzOi8vc2FibGllci5jb20iLCJuYW1lIjoiU2FibGllciBMb2NrdXAgIzEiLCJpbWFnZSI6ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJeE1EQXdJaUJvWldsbmFIUTlJakV3TURBaUlIWnBaWGRDYjNnOUlqQWdNQ0F4TURBd0lERXdNREFpUGp4eVpXTjBJSGRwWkhSb1BTSXhNREFsSWlCb1pXbG5hSFE5SWpFd01DVWlJR1pwYkhSbGNqMGlkWEpzS0NOT2IybHpaU2tpTHo0OGNtVmpkQ0I0UFNJM01DSWdlVDBpTnpBaUlIZHBaSFJvUFNJNE5qQWlJR2hsYVdkb2REMGlPRFl3SWlCbWFXeHNQU0lqWm1abUlpQm1hV3hzTFc5d1lXTnBkSGs5SWk0d015SWdjbmc5SWpRMUlpQnllVDBpTkRVaUlITjBjbTlyWlQwaUkyWm1aaUlnYzNSeWIydGxMVzl3WVdOcGRIazlJaTR4SWlCemRISnZhMlV0ZDJsa2RHZzlJalFpTHo0OFpHVm1jejQ4WTJseVkyeGxJR2xrUFNKSGJHOTNJaUJ5UFNJMU1EQWlJR1pwYkd3OUluVnliQ2dqVW1Ga2FXRnNSMnh2ZHlraUx6NDhabWxzZEdWeUlHbGtQU0pPYjJselpTSStQR1psUm14dmIyUWdlRDBpTUNJZ2VUMGlNQ0lnZDJsa2RHZzlJakV3TUNVaUlHaGxhV2RvZEQwaU1UQXdKU0lnWm14dmIyUXRZMjlzYjNJOUltaHpiQ2d5TXpBc01qRWxMREV4SlNraUlHWnNiMjlrTFc5d1lXTnBkSGs5SWpFaUlISmxjM1ZzZEQwaVpteHZiMlJHYVd4c0lpOCtQR1psVkhWeVluVnNaVzVqWlNCaVlYTmxSbkpsY1hWbGJtTjVQU0l1TkNJZ2JuVnRUMk4wWVhabGN6MGlNeUlnY21WemRXeDBQU0pPYjJselpTSWdkSGx3WlQwaVpuSmhZM1JoYkU1dmFYTmxJaTgrUEdabFFteGxibVFnYVc0OUlrNXZhWE5sSWlCcGJqSTlJbVpzYjI5a1JtbHNiQ0lnYlc5a1pUMGljMjltZEMxc2FXZG9kQ0l2UGp3dlptbHNkR1Z5UGp4d1lYUm9JR2xrUFNKTWIyZHZJaUJtYVd4c1BTSWpabVptSWlCbWFXeHNMVzl3WVdOcGRIazlJaTR4SWlCa1BTSnRNVE16TGpVMU9Td3hNalF1TURNMFl5MHVNREV6TERJdU5ERXlMVEV1TURVNUxEUXVPRFE0TFRJdU9USXpMRFl1TkRBeUxUSXVOVFU0TERFdU9ERTVMVFV1TVRZNExETXVORE01TFRjdU9EZzRMRFF1T1RrMkxURTBMalEwTERndU1qWXlMVE14TGpBME55d3hNaTQxTmpVdE5EY3VOamMwTERFeUxqVTJPUzA0TGpnMU9DNHdNell0TVRjdU9ETTRMVEV1TWpjeUxUSTJMak15T0MwekxqWTJNeTA1TGpnd05pMHlMamMyTmkweE9TNHdPRGN0Tnk0eE1UTXRNamN1TlRZeUxURXlMamMzT0MweE15NDROREl0T0M0d01qVXNPUzQwTmpndE1qZ3VOakEyTERFMkxqRTFNeTB6TlM0eU5qVm9NR015TGpBek5TMHhMamd6T0N3MExqSTFNaTB6TGpVME5pdzJMalEyTXkwMUxqSXlOR2d3WXpZdU5ESTVMVFV1TmpVMUxERTJMakl4T0MweUxqZ3pOU3d5TUM0ek5UZ3NOQzR4Tnl3MExqRTBNeXcxTGpBMU55dzRMamd4Tml3NUxqWTBPU3d4TXk0NU1pd3hNeTQzTXpSb0xqQXpOMk0xTGpjek5pdzJMalEyTVN3eE5TNHpOVGN0TWk0eU5UTXNPUzR6T0MwNExqUTRMREFzTUMwekxqVXhOUzB6TGpVeE5TMHpMalV4TlMwekxqVXhOUzB4TVM0ME9TMHhNUzQwTnpndE5USXVOalUyTFRVeUxqWTJOQzAyTkM0NE16Y3ROalF1T0RNM2JDNHdORGt0TGpBek4yTXRNUzQzTWpVdE1TNDJNRFl0TWk0M01Ua3RNeTQ0TkRjdE1pNDNOVEV0Tmk0eU1EUm9NR010TGpBME5pMHlMak0zTlN3eExqQTJNaTAwTGpVNE1pd3lMamN5TmkwMkxqSXlPV2d3YkM0eE9EVXRMakUwT0dnd1l5NHdPVGt0TGpBMk1pd3VNakl5TFM0eE5EZ3NMak0zTFM0eU5UbG9NR015TGpBMkxURXVNell5TERNdU9UVXhMVEl1TmpJeExEWXVNRFEwTFRNdU9EUXlRelUzTGpjMk15MHpMalEzTXl3NU55NDNOaTB5TGpNME1Td3hNamd1TmpNM0xERTRMak16TW1NeE5pNDJOekVzT1M0NU5EWXRNall1TXpRMExEVTBMamd4TXkwek9DNDJOVEVzTkRBdU1UazVMVFl1TWprNUxUWXVNRGsyTFRFNExqQTJNeTB4Tnk0M05ETXRNVGt1TmpZNExURTRMamd4TVMwMkxqQXhOaTAwTGpBME55MHhNeTR3TmpFc05DNDNOell0Tnk0M05USXNPUzQzTlRGc05qZ3VNalUwTERZNExqTTNNV014TGpjeU5Dd3hMall3TVN3eUxqY3hOQ3d6TGpnMExESXVOek00TERZdU1Ua3lXaUl2UGp4d1lYUm9JR2xrUFNKR2JHOWhkR2x1WjFSbGVIUWlJR1pwYkd3OUltNXZibVVpSUdROUlrMHhNalVnTkRWb056VXdjemd3SURBZ09EQWdPREIyTnpVd2N6QWdPREFnTFRnd0lEZ3dhQzAzTlRCekxUZ3dJREFnTFRnd0lDMDRNSFl0TnpVd2N6QWdMVGd3SURnd0lDMDRNQ0l2UGp4eVlXUnBZV3hIY21Ga2FXVnVkQ0JwWkQwaVVtRmthV0ZzUjJ4dmR5SStQSE4wYjNBZ2IyWm1jMlYwUFNJd0pTSWdjM1J2Y0MxamIyeHZjajBpYUhOc0tEWXpMRGsxSlN3MU55VXBJaUJ6ZEc5d0xXOXdZV05wZEhrOUlpNDJJaTgrUEhOMGIzQWdiMlptYzJWMFBTSXhNREFsSWlCemRHOXdMV052Ykc5eVBTSm9jMndvTWpNd0xESXhKU3d4TVNVcElpQnpkRzl3TFc5d1lXTnBkSGs5SWpBaUx6NDhMM0poWkdsaGJFZHlZV1JwWlc1MFBqeHNhVzVsWVhKSGNtRmthV1Z1ZENCcFpEMGlVMkZ1WkZSdmNDSWdlREU5SWpBbElpQjVNVDBpTUNVaVBqeHpkRzl3SUc5bVpuTmxkRDBpTUNVaUlITjBiM0F0WTI5c2IzSTlJbWh6YkNnMk15dzVOU1VzTlRjbEtTSXZQanh6ZEc5d0lHOW1abk5sZEQwaU1UQXdKU0lnYzNSdmNDMWpiMnh2Y2owaWFITnNLREl6TUN3eU1TVXNNVEVsS1NJdlBqd3ZiR2x1WldGeVIzSmhaR2xsYm5RK1BHeHBibVZoY2tkeVlXUnBaVzUwSUdsa1BTSlRZVzVrUW05MGRHOXRJaUI0TVQwaU1UQXdKU0lnZVRFOUlqRXdNQ1VpUGp4emRHOXdJRzltWm5ObGREMGlNVEFsSWlCemRHOXdMV052Ykc5eVBTSm9jMndvTWpNd0xESXhKU3d4TVNVcElpOCtQSE4wYjNBZ2IyWm1jMlYwUFNJeE1EQWxJaUJ6ZEc5d0xXTnZiRzl5UFNKb2Myd29Oak1zT1RVbExEVTNKU2tpTHo0OFlXNXBiV0YwWlNCaGRIUnlhV0oxZEdWT1lXMWxQU0o0TVNJZ1pIVnlQU0kyY3lJZ2NtVndaV0YwUTI5MWJuUTlJbWx1WkdWbWFXNXBkR1VpSUhaaGJIVmxjejBpTXpBbE96WXdKVHN4TWpBbE96WXdKVHN6TUNVN0lpOCtQQzlzYVc1bFlYSkhjbUZrYVdWdWRENDhiR2x1WldGeVIzSmhaR2xsYm5RZ2FXUTlJa2h2ZFhKbmJHRnpjMU4wY205clpTSWdaM0poWkdsbGJuUlVjbUZ1YzJadmNtMDlJbkp2ZEdGMFpTZzVNQ2tpSUdkeVlXUnBaVzUwVlc1cGRITTlJblZ6WlhKVGNHRmpaVTl1VlhObElqNDhjM1J2Y0NCdlptWnpaWFE5SWpVd0pTSWdjM1J2Y0MxamIyeHZjajBpYUhOc0tEWXpMRGsxSlN3MU55VXBJaTgrUEhOMGIzQWdiMlptYzJWMFBTSTRNQ1VpSUhOMGIzQXRZMjlzYjNJOUltaHpiQ2d5TXpBc01qRWxMREV4SlNraUx6NDhMMnhwYm1WaGNrZHlZV1JwWlc1MFBqeG5JR2xrUFNKSWIzVnlaMnhoYzNNaVBqeHdZWFJvSUdROUlrMGdOVEFzTXpZd0lHRWdNekF3TERNd01DQXdJREVzTVNBMk1EQXNNQ0JoSURNd01Dd3pNREFnTUNBeExERWdMVFl3TUN3d0lpQm1hV3hzUFNJalptWm1JaUJtYVd4c0xXOXdZV05wZEhrOUlpNHdNaUlnYzNSeWIydGxQU0oxY213b0kwaHZkWEpuYkdGemMxTjBjbTlyWlNraUlITjBjbTlyWlMxM2FXUjBhRDBpTkNJdlBqeHdZWFJvSUdROUltMDFOallzTVRZeExqSXdNWFl0TlRNdU9USTBZekF0TVRrdU16Z3lMVEl5TGpVeE15MHpOeTQxTmpNdE5qTXVNems0TFRVeExqRTVPQzAwTUM0M05UWXRNVE11TlRreUxUazBMamswTmkweU1TNHdOemt0TVRVeUxqVTROeTB5TVM0d056bHpMVEV4TVM0NE16Z3NOeTQwT0RjdE1UVXlMall3TWl3eU1TNHdOemxqTFRRd0xqZzVNeXd4TXk0Mk16WXROak11TkRFekxETXhMamd4TmkwMk15NDBNVE1zTlRFdU1UazRkalV6TGpreU5HTXdMREUzTGpFNE1Td3hOeTQzTURRc016TXVOREkzTERVd0xqSXlNeXcwTmk0ek9UUjJNamcwTGpnd09XTXRNekl1TlRFNUxERXlMamsyTFRVd0xqSXlNeXd5T1M0eU1EWXROVEF1TWpJekxEUTJMak01TkhZMU15NDVNalJqTUN3eE9TNHpPRElzTWpJdU5USXNNemN1TlRZekxEWXpMalF4TXl3MU1TNHhPVGdzTkRBdU56WXpMREV6TGpVNU1pdzVOQzQ1TlRRc01qRXVNRGM1TERFMU1pNDJNRElzTWpFdU1EYzVjekV4TVM0NE16RXROeTQwT0Rjc01UVXlMalU0TnkweU1TNHdOemxqTkRBdU9EZzJMVEV6TGpZek5pdzJNeTR6T1RndE16RXVPREUyTERZekxqTTVPQzAxTVM0eE9UaDJMVFV6TGpreU5HTXdMVEUzTGpFNU5pMHhOeTQzTURRdE16TXVORE0xTFRVd0xqSXlNeTAwTmk0ME1ERldNakEzTGpZd00yTXpNaTQxTVRrdE1USXVPVFkzTERVd0xqSXlNeTB5T1M0eU1EWXNOVEF1TWpJekxUUTJMalF3TVZwdExUTTBOeTQwTmpJc05UY3VOemt6YkRFek1DNDVOVGtzTVRNeExqQXlOeTB4TXpBdU9UVTVMREV6TVM0d01UTldNakU0TGprNU5GcHRNall5TGpreU5DNHdNakoyTWpZeUxqQXhPR3d0TVRNd0xqa3pOeTB4TXpFdU1EQTJMREV6TUM0NU16Y3RNVE14TGpBeE0xb2lJR1pwYkd3OUlpTXhOakU0TWpJaVBqd3ZjR0YwYUQ0OGNHOXNlV2R2YmlCd2IybHVkSE05SWpNMU1DQXpOVEF1TURJMklEUXhOUzR3TXlBeU9EUXVPVGM0SURJNE5TQXlPRFF1T1RjNElETTFNQ0F6TlRBdU1ESTJJaUJtYVd4c1BTSjFjbXdvSTFOaGJtUkNiM1IwYjIwcElpOCtQSEJoZEdnZ1pEMGliVFF4Tmk0ek5ERXNNamd4TGprM05XTXdMQzQ1TVRRdExqTTFOQ3d4TGpnd09TMHhMakF6TlN3eUxqWTRMVFV1TlRReUxEY3VNRGMyTFRNeUxqWTJNU3d4TWk0ME5TMDJOUzR5T0N3eE1pNDBOUzB6TWk0Mk1qUXNNQzAxT1M0M016Z3ROUzR6TnpRdE5qVXVNamd0TVRJdU5EVXRMalk0TVMwdU9EY3lMVEV1TURNMUxURXVOelkzTFRFdU1ETTFMVEl1Tmpnc01DMHVPVEUwTGpNMU5DMHhMamd3T0N3eExqQXpOUzB5TGpZM05pdzFMalUwTWkwM0xqQTNOaXd6TWk0Mk5UWXRNVEl1TkRVc05qVXVNamd0TVRJdU5EVXNNekl1TmpFNUxEQXNOVGt1TnpNNExEVXVNemMwTERZMUxqSTRMREV5TGpRMUxqWTRNUzQ0Tmpjc01TNHdNelVzTVM0M05qSXNNUzR3TXpVc01pNDJOelphSWlCbWFXeHNQU0oxY213b0kxTmhibVJVYjNBcElpOCtQSEJoZEdnZ1pEMGliVFE0TVM0ME5pdzFNRFF1TVRBeGRqVTRMalEwT1dNdE1pNHpOUzQzTnkwMExqZ3lMREV1TlRFdE55NHpPU3d5TGpJekxUTXdMak1zT0M0MU5DMDNOQzQyTlN3eE15NDVNaTB4TWpRdU1EWXNNVE11T1RJdE5UTXVOaXd3TFRFd01TNHlOQzAyTGpNekxURXpNUzQwTnkweE5pNHhObll0TlRndU5ETTVhREkyTWk0NU1sb2lJR1pwYkd3OUluVnliQ2dqVTJGdVpFSnZkSFJ2YlNraUx6NDhaV3hzYVhCelpTQmplRDBpTXpVd0lpQmplVDBpTlRBMExqRXdNU0lnY25nOUlqRXpNUzQwTmpJaUlISjVQU0l5T0M0eE1EZ2lJR1pwYkd3OUluVnliQ2dqVTJGdVpGUnZjQ2tpTHo0OFp5Qm1hV3hzUFNKdWIyNWxJaUJ6ZEhKdmEyVTlJblZ5YkNnalNHOTFjbWRzWVhOelUzUnliMnRsS1NJZ2MzUnliMnRsTFd4cGJtVmpZWEE5SW5KdmRXNWtJaUJ6ZEhKdmEyVXRiV2wwWlhKc2FXMXBkRDBpTVRBaUlITjBjbTlyWlMxM2FXUjBhRDBpTkNJK1BIQmhkR2dnWkQwaWJUVTJOUzQyTkRFc01UQTNMakk0WXpBc09TNDFNemN0TlM0MU5pd3hPQzQyTWprdE1UVXVOamMyTERJMkxqazNNMmd0TGpBeU0yTXRPUzR5TURRc055NDFPVFl0TWpJdU1UazBMREUwTGpVMk1pMHpPQzR4T1Rjc01qQXVOVGt5TFRNNUxqVXdOQ3d4TkM0NU16WXRPVGN1TXpJMUxESTBMak0xTlMweE5qRXVOek16TERJMExqTTFOUzA1TUM0ME9Dd3dMVEUyTnk0NU5EZ3RNVGd1TlRneUxURTVPUzQ1TlRNdE5EUXVPVFE0YUMwdU1ESXpZeTB4TUM0eE1UVXRPQzR6TkRRdE1UVXVOamMyTFRFM0xqUXpOeTB4TlM0Mk56WXRNall1T1RjekxEQXRNemt1TnpNMUxEazJMalUxTkMwM01TNDVNakVzTWpFMUxqWTFNaTAzTVM0NU1qRnpNakUxTGpZeU9Td3pNaTR4T0RVc01qRTFMall5T1N3M01TNDVNakZhSWk4K1BIQmhkR2dnWkQwaWJURXpOQzR6Tml3eE5qRXVNakF6WXpBc016a3VOek0xTERrMkxqVTFOQ3czTVM0NU1qRXNNakUxTGpZMU1pdzNNUzQ1TWpGek1qRTFMall5T1Mwek1pNHhPRFlzTWpFMUxqWXlPUzAzTVM0NU1qRWlMejQ4YkdsdVpTQjRNVDBpTVRNMExqTTJJaUI1TVQwaU1UWXhMakl3TXlJZ2VESTlJakV6TkM0ek5pSWdlVEk5SWpFd055NHlPQ0l2UGp4c2FXNWxJSGd4UFNJMU5qVXVOalFpSUhreFBTSXhOakV1TWpBeklpQjRNajBpTlRZMUxqWTBJaUI1TWowaU1UQTNMakk0SWk4K1BHeHBibVVnZURFOUlqRTROQzQxT0RRaUlIa3hQU0l5TURZdU9ESXpJaUI0TWowaU1UZzBMalU0TlNJZ2VUSTlJalV6Tnk0MU56a2lMejQ4YkdsdVpTQjRNVDBpTWpFNExqRTRNU0lnZVRFOUlqSXhPQzR4TVRnaUlIZ3lQU0l5TVRndU1UZ3hJaUI1TWowaU5UWXlMalV6TnlJdlBqeHNhVzVsSUhneFBTSTBPREV1T0RFNElpQjVNVDBpTWpFNExqRTBNaUlnZURJOUlqUTRNUzQ0TVRraUlIa3lQU0kxTmpJdU5ESTRJaTgrUEd4cGJtVWdlREU5SWpVeE5TNDBNVFVpSUhreFBTSXlNRGN1TXpVeUlpQjRNajBpTlRFMUxqUXhOaUlnZVRJOUlqVXpOeTQxTnpraUx6NDhjR0YwYUNCa1BTSnRNVGcwTGpVNExEVXpOeTQxT0dNd0xEVXVORFVzTkM0eU55d3hNQzQyTlN3eE1pNHdNeXd4TlM0ME1tZ3VNREpqTlM0MU1Td3pMak01TERFeUxqYzVMRFl1TlRVc01qRXVOVFVzT1M0ME1pd3pNQzR5TVN3NUxqa3NOemd1TURJc01UWXVNamdzTVRNeExqZ3pMREUyTGpJNExEUTVMalF4TERBc09UTXVOell0TlM0ek9Dd3hNalF1TURZdE1UTXVPVElzTWk0M0xTNDNOaXcxTGpJNUxURXVOVFFzTnk0M05TMHlMak0xTERndU56Y3RNaTQ0Tnl3eE5pNHdOUzAyTGpBMExESXhMalUyTFRrdU5ETm9NR00zTGpjMkxUUXVOemNzTVRJdU1EUXRPUzQ1Tnl3eE1pNHdOQzB4TlM0ME1pSXZQanh3WVhSb0lHUTlJbTB4T0RRdU5UZ3lMRFE1TWk0Mk5UWmpMVE14TGpNMU5Dd3hNaTQwT0RVdE5UQXVNakl6TERJNExqVTRMVFV3TGpJeU15dzBOaTR4TkRJc01DdzVMalV6Tml3MUxqVTJOQ3d4T0M0Mk1qY3NNVFV1TmpjM0xESTJMamsyT1dndU1ESXlZemd1TlRBekxEY3VNREExTERJd0xqSXhNeXd4TXk0ME5qTXNNelF1TlRJMExERTVMakUxT1N3NUxqazVPU3d6TGprNU1Td3lNUzR5Tmprc055NDJNRGtzTXpNdU5UazNMREV3TGpjNE9Dd3pOaTQwTlN3NUxqUXdOeXc0TWk0eE9ERXNNVFV1TURBeUxERXpNUzQ0TXpVc01UVXVNREF5Y3prMUxqTTJNeTAxTGpVNU5Td3hNekV1T0RBM0xURTFMakF3TW1NeE1DNDRORGN0TWk0M09Td3lNQzQ0TmpjdE5TNDVNallzTWprdU9USTBMVGt1TXpRNUxERXVNalEwTFM0ME5qY3NNaTQwTnpNdExqazBNaXd6TGpZM015MHhMalF5TkN3eE5DNHpNall0TlM0Mk9UWXNNall1TURNMUxURXlMakUyTVN3ek5DNDFNalF0TVRrdU1UY3phQzR3TWpKak1UQXVNVEUwTFRndU16UXlMREUxTGpZM055MHhOeTQwTXpNc01UVXVOamMzTFRJMkxqazJPU3d3TFRFM0xqVTJNaTB4T0M0NE5qa3RNek11TmpZMUxUVXdMakl5TXkwME5pNHhOU0l2UGp4d1lYUm9JR1E5SW0weE16UXVNellzTlRreUxqY3lZekFzTXprdU56TTFMRGsyTGpVMU5DdzNNUzQ1TWpFc01qRTFMalkxTWl3M01TNDVNakZ6TWpFMUxqWXlPUzB6TWk0eE9EWXNNakUxTGpZeU9TMDNNUzQ1TWpFaUx6NDhiR2x1WlNCNE1UMGlNVE0wTGpNMklpQjVNVDBpTlRreUxqY3lJaUI0TWowaU1UTTBMak0ySWlCNU1qMGlOVE00TGpjNU55SXZQanhzYVc1bElIZ3hQU0kxTmpVdU5qUWlJSGt4UFNJMU9USXVOeklpSUhneVBTSTFOalV1TmpRaUlIa3lQU0kxTXpndU56azNJaTgrUEhCdmJIbHNhVzVsSUhCdmFXNTBjejBpTkRneExqZ3lNaUEwT0RFdU9UQXhJRFE0TVM0M09UZ2dORGd4TGpnM055QTBPREV1TnpjMUlEUTRNUzQ0TlRRZ016VXdMakF4TlNBek5UQXVNREkySURJeE9DNHhPRFVnTWpFNExqRXlPU0l2UGp4d2IyeDViR2x1WlNCd2IybHVkSE05SWpJeE9DNHhPRFVnTkRneExqa3dNU0F5TVRndU1qTXhJRFE0TVM0NE5UUWdNelV3TGpBeE5TQXpOVEF1TURJMklEUTRNUzQ0TWpJZ01qRTRMakUxTWlJdlBqd3ZaejQ4TDJjK1BHY2dhV1E5SWxCeWIyZHlaWE56SWlCbWFXeHNQU0lqWm1abUlqNDhjbVZqZENCM2FXUjBhRDBpTWpBNElpQm9aV2xuYUhROUlqRXdNQ0lnWm1sc2JDMXZjR0ZqYVhSNVBTSXVNRE1pSUhKNFBTSXhOU0lnY25rOUlqRTFJaUJ6ZEhKdmEyVTlJaU5tWm1ZaUlITjBjbTlyWlMxdmNHRmphWFI1UFNJdU1TSWdjM1J5YjJ0bExYZHBaSFJvUFNJMElpOCtQSFJsZUhRZ2VEMGlNakFpSUhrOUlqTTBJaUJtYjI1MExXWmhiV2xzZVQwaUowTnZkWEpwWlhJZ1RtVjNKeXhCY21saGJDeHRiMjV2YzNCaFkyVWlJR1p2Ym5RdGMybDZaVDBpTWpKd2VDSStVSEp2WjNKbGMzTThMM1JsZUhRK1BIUmxlSFFnZUQwaU1qQWlJSGs5SWpjeUlpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5eEJjbWxoYkN4dGIyNXZjM0JoWTJVaUlHWnZiblF0YzJsNlpUMGlNalp3ZUNJK01qSXVPVGdsUEM5MFpYaDBQanhuSUdacGJHdzlJbTV2Ym1VaVBqeGphWEpqYkdVZ1kzZzlJakUyTmlJZ1kzazlJalV3SWlCeVBTSXlNaUlnYzNSeWIydGxQU0pvYzJ3b01qTXdMREl4SlN3eE1TVXBJaUJ6ZEhKdmEyVXRkMmxrZEdnOUlqRXdJaTgrUEdOcGNtTnNaU0JqZUQwaU1UWTJJaUJqZVQwaU5UQWlJSEJoZEdoTVpXNW5kR2c5SWpFd01EQXdJaUJ5UFNJeU1pSWdjM1J5YjJ0bFBTSm9jMndvTmpNc09UVWxMRFUzSlNraUlITjBjbTlyWlMxa1lYTm9ZWEp5WVhrOUlqRXdNREF3SWlCemRISnZhMlV0WkdGemFHOW1abk5sZEQwaU56Y3dNaUlnYzNSeWIydGxMV3hwYm1WallYQTlJbkp2ZFc1a0lpQnpkSEp2YTJVdGQybGtkR2c5SWpVaUlIUnlZVzV6Wm05eWJUMGljbTkwWVhSbEtDMDVNQ2tpSUhSeVlXNXpabTl5YlMxdmNtbG5hVzQ5SWpFMk5pQTFNQ0l2UGp3dlp6NDhMMmMrUEdjZ2FXUTlJbE4wWVhSMWN5SWdabWxzYkQwaUkyWm1aaUkrUEhKbFkzUWdkMmxrZEdnOUlqRTROQ0lnYUdWcFoyaDBQU0l4TURBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpBeklpQnllRDBpTVRVaUlISjVQU0l4TlNJZ2MzUnliMnRsUFNJalptWm1JaUJ6ZEhKdmEyVXRiM0JoWTJsMGVUMGlMakVpSUhOMGNtOXJaUzEzYVdSMGFEMGlOQ0l2UGp4MFpYaDBJSGc5SWpJd0lpQjVQU0l6TkNJZ1ptOXVkQzFtWVcxcGJIazlJaWREYjNWeWFXVnlJRTVsZHljc1FYSnBZV3dzYlc5dWIzTndZV05sSWlCbWIyNTBMWE5wZW1VOUlqSXljSGdpUGxOMFlYUjFjend2ZEdWNGRENDhkR1Y0ZENCNFBTSXlNQ0lnZVQwaU56SWlJR1p2Ym5RdFptRnRhV3g1UFNJblEyOTFjbWxsY2lCT1pYY25MRUZ5YVdGc0xHMXZibTl6Y0dGalpTSWdabTl1ZEMxemFYcGxQU0l5Tm5CNElqNVRkSEpsWVcxcGJtYzhMM1JsZUhRK1BDOW5QanhuSUdsa1BTSkJiVzkxYm5RaUlHWnBiR3c5SWlObVptWWlQanh5WldOMElIZHBaSFJvUFNJeE1qQWlJR2hsYVdkb2REMGlNVEF3SWlCbWFXeHNMVzl3WVdOcGRIazlJaTR3TXlJZ2NuZzlJakUxSWlCeWVUMGlNVFVpSUhOMGNtOXJaVDBpSTJabVppSWdjM1J5YjJ0bExXOXdZV05wZEhrOUlpNHhJaUJ6ZEhKdmEyVXRkMmxrZEdnOUlqUWlMejQ4ZEdWNGRDQjRQU0l5TUNJZ2VUMGlNelFpSUdadmJuUXRabUZ0YVd4NVBTSW5RMjkxY21sbGNpQk9aWGNuTEVGeWFXRnNMRzF2Ym05emNHRmpaU0lnWm05dWRDMXphWHBsUFNJeU1uQjRJajVCYlc5MWJuUThMM1JsZUhRK1BIUmxlSFFnZUQwaU1qQWlJSGs5SWpjeUlpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5eEJjbWxoYkN4dGIyNXZjM0JoWTJVaUlHWnZiblF0YzJsNlpUMGlNalp3ZUNJK0ppTTRPREExT3lBeE1FczhMM1JsZUhRK1BDOW5QanhuSUdsa1BTSkVkWEpoZEdsdmJpSWdabWxzYkQwaUkyWm1aaUkrUEhKbFkzUWdkMmxrZEdnOUlqRTFNaUlnYUdWcFoyaDBQU0l4TURBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpBeklpQnllRDBpTVRVaUlISjVQU0l4TlNJZ2MzUnliMnRsUFNJalptWm1JaUJ6ZEhKdmEyVXRiM0JoWTJsMGVUMGlMakVpSUhOMGNtOXJaUzEzYVdSMGFEMGlOQ0l2UGp4MFpYaDBJSGc5SWpJd0lpQjVQU0l6TkNJZ1ptOXVkQzFtWVcxcGJIazlJaWREYjNWeWFXVnlJRTVsZHljc1FYSnBZV3dzYlc5dWIzTndZV05sSWlCbWIyNTBMWE5wZW1VOUlqSXljSGdpUGtSMWNtRjBhVzl1UEM5MFpYaDBQangwWlhoMElIZzlJakl3SWlCNVBTSTNNaUlnWm05dWRDMW1ZVzFwYkhrOUlpZERiM1Z5YVdWeUlFNWxkeWNzUVhKcFlXd3NiVzl1YjNOd1lXTmxJaUJtYjI1MExYTnBlbVU5SWpJMmNIZ2lQaVpzZERzZ01TQkVZWGs4TDNSbGVIUStQQzluUGp3dlpHVm1jejQ4ZEdWNGRDQjBaWGgwTFhKbGJtUmxjbWx1WnowaWIzQjBhVzFwZW1WVGNHVmxaQ0krUEhSbGVIUlFZWFJvSUhOMFlYSjBUMlptYzJWMFBTSXRNVEF3SlNJZ2FISmxaajBpSTBac2IyRjBhVzVuVkdWNGRDSWdabWxzYkQwaUkyWm1aaUlnWm05dWRDMW1ZVzFwYkhrOUlpZERiM1Z5YVdWeUlFNWxkeWNzUVhKcFlXd3NiVzl1YjNOd1lXTmxJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDRJaUJtYjI1MExYTnBlbVU5SWpJMmNIZ2lQanhoYm1sdFlYUmxJR0ZrWkdsMGFYWmxQU0p6ZFcwaUlHRjBkSEpwWW5WMFpVNWhiV1U5SW5OMFlYSjBUMlptYzJWMElpQmlaV2RwYmowaU1ITWlJR1IxY2owaU5UQnpJaUJtY205dFBTSXdKU0lnY21Wd1pXRjBRMjkxYm5ROUltbHVaR1ZtYVc1cGRHVWlJSFJ2UFNJeE1EQWxJaTgrTUhnNU1qTmlOV0ZpTXpjeE5HWmtNelF6TXpFMllXWTFZVFUwTXpRMU9ESm1aREUyTnpJeU5USXpJT0tBb2lCVFlXSnNhV1Z5SUV4dlkydDFjRHd2ZEdWNGRGQmhkR2crUEhSbGVIUlFZWFJvSUhOMFlYSjBUMlptYzJWMFBTSXdKU0lnYUhKbFpqMGlJMFpzYjJGMGFXNW5WR1Y0ZENJZ1ptbHNiRDBpSTJabVppSWdabTl1ZEMxbVlXMXBiSGs5SWlkRGIzVnlhV1Z5SUU1bGR5Y3NRWEpwWVd3c2JXOXViM053WVdObElpQm1hV3hzTFc5d1lXTnBkSGs5SWk0NElpQm1iMjUwTFhOcGVtVTlJakkyY0hnaVBqeGhibWx0WVhSbElHRmtaR2wwYVhabFBTSnpkVzBpSUdGMGRISnBZblYwWlU1aGJXVTlJbk4wWVhKMFQyWm1jMlYwSWlCaVpXZHBiajBpTUhNaUlHUjFjajBpTlRCeklpQm1jbTl0UFNJd0pTSWdjbVZ3WldGMFEyOTFiblE5SW1sdVpHVm1hVzVwZEdVaUlIUnZQU0l4TURBbElpOCtNSGc1TWpOaU5XRmlNemN4Tkdaa016UXpNekUyWVdZMVlUVTBNelExT0RKbVpERTJOekl5TlRJeklPS0FvaUJUWVdKc2FXVnlJRXh2WTJ0MWNEd3ZkR1Y0ZEZCaGRHZytQSFJsZUhSUVlYUm9JSE4wWVhKMFQyWm1jMlYwUFNJdE5UQWxJaUJvY21WbVBTSWpSbXh2WVhScGJtZFVaWGgwSWlCbWFXeHNQU0lqWm1abUlpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5eEJjbWxoYkN4dGIyNXZjM0JoWTJVaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpnaUlHWnZiblF0YzJsNlpUMGlNalp3ZUNJK1BHRnVhVzFoZEdVZ1lXUmthWFJwZG1VOUluTjFiU0lnWVhSMGNtbGlkWFJsVG1GdFpUMGljM1JoY25SUFptWnpaWFFpSUdKbFoybHVQU0l3Y3lJZ1pIVnlQU0kxTUhNaUlHWnliMjA5SWpBbElpQnlaWEJsWVhSRGIzVnVkRDBpYVc1a1pXWnBibWwwWlNJZ2RHODlJakV3TUNVaUx6NHdlR1kyTWpnME9XWTVZVEJpTldKbU1qa3hNMkl6T1RZd09UaG1OMk0zTURFNVlqVXhZVGd5TUdFZzRvQ2lJRVJCU1R3dmRHVjRkRkJoZEdnK1BIUmxlSFJRWVhSb0lITjBZWEowVDJabWMyVjBQU0kxTUNVaUlHaHlaV1k5SWlOR2JHOWhkR2x1WjFSbGVIUWlJR1pwYkd3OUlpTm1abVlpSUdadmJuUXRabUZ0YVd4NVBTSW5RMjkxY21sbGNpQk9aWGNuTEVGeWFXRnNMRzF2Ym05emNHRmpaU0lnWm1sc2JDMXZjR0ZqYVhSNVBTSXVPQ0lnWm05dWRDMXphWHBsUFNJeU5uQjRJajQ4WVc1cGJXRjBaU0JoWkdScGRHbDJaVDBpYzNWdElpQmhkSFJ5YVdKMWRHVk9ZVzFsUFNKemRHRnlkRTltWm5ObGRDSWdZbVZuYVc0OUlqQnpJaUJrZFhJOUlqVXdjeUlnWm5KdmJUMGlNQ1VpSUhKbGNHVmhkRU52ZFc1MFBTSnBibVJsWm1sdWFYUmxJaUIwYnowaU1UQXdKU0l2UGpCNFpqWXlPRFE1WmpsaE1HSTFZbVl5T1RFellqTTVOakE1T0dZM1l6Y3dNVGxpTlRGaE9ESXdZU0RpZ0tJZ1JFRkpQQzkwWlhoMFVHRjBhRDQ4TDNSbGVIUStQSFZ6WlNCb2NtVm1QU0lqUjJ4dmR5SWdabWxzYkMxdmNHRmphWFI1UFNJdU9TSXZQangxYzJVZ2FISmxaajBpSTBkc2IzY2lJSGc5SWpFd01EQWlJSGs5SWpFd01EQWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqa2lMejQ4ZFhObElHaHlaV1k5SWlOTWIyZHZJaUI0UFNJeE56QWlJSGs5SWpFM01DSWdkSEpoYm5ObWIzSnRQU0p6WTJGc1pTZ3VOaWtpTHo0OGRYTmxJR2h5WldZOUlpTkliM1Z5WjJ4aGMzTWlJSGc5SWpFMU1DSWdlVDBpT1RBaUlIUnlZVzV6Wm05eWJUMGljbTkwWVhSbEtERXdLU0lnZEhKaGJuTm1iM0p0TFc5eWFXZHBiajBpTlRBd0lEVXdNQ0l2UGp4MWMyVWdhSEpsWmowaUkxQnliMmR5WlhOeklpQjRQU0l4TkRRaUlIazlJamM1TUNJdlBqeDFjMlVnYUhKbFpqMGlJMU4wWVhSMWN5SWdlRDBpTXpZNElpQjVQU0kzT1RBaUx6NDhkWE5sSUdoeVpXWTlJaU5CYlc5MWJuUWlJSGc5SWpVMk9DSWdlVDBpTnprd0lpOCtQSFZ6WlNCb2NtVm1QU0lqUkhWeVlYUnBiMjRpSUhnOUlqY3dOQ0lnZVQwaU56a3dJaTgrUEM5emRtYysifQ=="; assertEq(actualTokenURI, expectedTokenURI, "token URI"); } } diff --git a/tests/integration/concrete/nft-descriptor/map-symbol/mapSymbol.t.sol b/tests/integration/concrete/nft-descriptor/map-symbol/mapSymbol.t.sol deleted file mode 100644 index d27c69886..000000000 --- a/tests/integration/concrete/nft-descriptor/map-symbol/mapSymbol.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.22 <0.9.0; - -import { IERC721Metadata } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; -import { MockERC721 } from "forge-std/src/mocks/MockERC721.sol"; - -import { Errors } from "src/libraries/Errors.sol"; - -import { Base_Test } from "tests/Base.t.sol"; - -contract MapSymbol_Integration_Concrete_Test is Base_Test { - function test_RevertGiven_UnknownNFTContract() external { - MockERC721 nft = new MockERC721(); - nft.initialize("Foo", "FOO"); - vm.expectRevert(abi.encodeWithSelector(Errors.LockupNFTDescriptor_UnknownNFT.selector, nft, "FOO")); - nftDescriptorMock.mapSymbol_(IERC721Metadata(address(nft))); - } - - function test_GivenKnownNFTContract() external view { - string memory actualLockupModel = nftDescriptorMock.mapSymbol_(lockup); - string memory expectedLockupModel = "Sablier Lockup"; - assertEq(actualLockupModel, expectedLockupModel, "lockupModel"); - } -} diff --git a/tests/integration/concrete/nft-descriptor/map-symbol/mapSymbol.tree b/tests/integration/concrete/nft-descriptor/map-symbol/mapSymbol.tree deleted file mode 100644 index 1193e42ce..000000000 --- a/tests/integration/concrete/nft-descriptor/map-symbol/mapSymbol.tree +++ /dev/null @@ -1,5 +0,0 @@ -MapSymbol_Integration_Concrete_Test -├── given unknown NFT contract -│ └── it should revert -└── given known NFT contract - └── it should map the ERC-721 symbol to Lockup Linear diff --git a/tests/mocks/NFTDescriptorMock.sol b/tests/mocks/NFTDescriptorMock.sol index 811a07b5f..596690691 100644 --- a/tests/mocks/NFTDescriptorMock.sol +++ b/tests/mocks/NFTDescriptorMock.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.22; -import { IERC721Metadata } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; import { NFTSVG } from "src/libraries/NFTSVG.sol"; import { SVGElements } from "src/libraries/SVGElements.sol"; import { LockupNFTDescriptor } from "src/LockupNFTDescriptor.sol"; @@ -51,7 +50,6 @@ contract NFTDescriptorMock is LockupNFTDescriptor { } function generateDescription_( - string memory lockupModel, string memory tokenSymbol, string memory lockupAddress, string memory tokenAddress, @@ -62,11 +60,7 @@ contract NFTDescriptorMock is LockupNFTDescriptor { pure returns (string memory) { - return generateDescription(lockupModel, tokenSymbol, lockupAddress, tokenAddress, streamId, isTransferable); - } - - function generateName_(string memory lockupModel, string memory streamId) external pure returns (string memory) { - return generateName(lockupModel, streamId); + return generateDescription(tokenSymbol, lockupAddress, tokenAddress, streamId, isTransferable); } function generateSVG_(NFTSVG.SVGParams memory params) external pure returns (string memory) { @@ -81,10 +75,6 @@ contract NFTDescriptorMock is LockupNFTDescriptor { return isAllowedCharacter(symbol); } - function mapSymbol_(IERC721Metadata nft) external view returns (string memory) { - return mapSymbol(nft); - } - function safeTokenDecimals_(address token) external view returns (uint8) { return safeTokenDecimals(token); } diff --git a/tests/unit/concrete/nft-descriptor/generateDescription.t.sol b/tests/unit/concrete/nft-descriptor/generateDescription.t.sol index 972b33d6f..9f7c3b3af 100644 --- a/tests/unit/concrete/nft-descriptor/generateDescription.t.sol +++ b/tests/unit/concrete/nft-descriptor/generateDescription.t.sol @@ -11,14 +11,12 @@ contract GenerateDescription_Unit_Concrete_Test is Base_Test { unicode"⚠️ WARNING: Transferring the NFT makes the new owner the recipient of the stream. The funds are not automatically withdrawn for the previous recipient."; function test_GenerateDescription_Empty() external view { - string memory actualDescription = nftDescriptorMock.generateDescription_("", "", "", "", "", true); + string memory actualDescription = nftDescriptorMock.generateDescription_("", "", "", "", true); string memory expectedDescription = string.concat( - "This NFT represents a payment stream in a Sablier Lockup ", - " contract. The owner of this NFT can withdraw the streamed tokens, which are denominated in ", - ".\\n\\n", - "- Stream ID: ", + "This NFT represents a stream in Sablier Lockup contract. The owner of this NFT can withdraw the streamed tokens, which are denominated in ", + ".\\n\\n- Stream ID: ", "\\n- ", - " Address: ", + "Sablier Lockup Address: ", "\\n- ", " Address: ", "\\n\\n", @@ -29,7 +27,6 @@ contract GenerateDescription_Unit_Concrete_Test is Base_Test { function test_GenerateDescription_NonTransferable() external view { string memory actualDescription = nftDescriptorMock.generateDescription_( - "Lockup Linear", dai.symbol(), "0x78B190C1E493752f85E02b00a0C98851A5638A30", "0xFEbD67A34821d1607a57DD31aae5f246D7dE2ca2", @@ -37,16 +34,12 @@ contract GenerateDescription_Unit_Concrete_Test is Base_Test { false ); string memory expectedDescription = string.concat( - "This NFT represents a payment stream in a Sablier Lockup ", - "Lockup Linear", - " contract. The owner of this NFT can withdraw the streamed tokens, which are denominated in ", + "This NFT represents a stream in Sablier Lockup contract. The owner of this NFT can withdraw the streamed tokens, which are denominated in ", dai.symbol(), - ".\\n\\n", - "- Stream ID: ", + ".\\n\\n- Stream ID: ", "42", "\\n- ", - "Lockup Linear", - " Address: ", + "Sablier Lockup Address: ", "0x78B190C1E493752f85E02b00a0C98851A5638A30", "\\n- ", "DAI", @@ -60,7 +53,6 @@ contract GenerateDescription_Unit_Concrete_Test is Base_Test { function test_GenerateDescription() external view { string memory actualDescription = nftDescriptorMock.generateDescription_( - "Lockup Linear", dai.symbol(), "0x78B190C1E493752f85E02b00a0C98851A5638A30", "0xFEbD67A34821d1607a57DD31aae5f246D7dE2ca2", @@ -68,16 +60,12 @@ contract GenerateDescription_Unit_Concrete_Test is Base_Test { true ); string memory expectedDescription = string.concat( - "This NFT represents a payment stream in a Sablier Lockup ", - "Lockup Linear", - " contract. The owner of this NFT can withdraw the streamed tokens, which are denominated in ", + "This NFT represents a stream in Sablier Lockup contract. The owner of this NFT can withdraw the streamed tokens, which are denominated in ", dai.symbol(), - ".\\n\\n", - "- Stream ID: ", + ".\\n\\n- Stream ID: ", "42", "\\n- ", - "Lockup Linear", - " Address: ", + "Sablier Lockup Address: ", "0x78B190C1E493752f85E02b00a0C98851A5638A30", "\\n- ", "DAI", diff --git a/tests/unit/concrete/nft-descriptor/generateName.t.sol b/tests/unit/concrete/nft-descriptor/generateName.t.sol deleted file mode 100644 index df500835d..000000000 --- a/tests/unit/concrete/nft-descriptor/generateName.t.sol +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.22 <0.9.0; - -import { Base_Test } from "tests/Base.t.sol"; - -contract GenerateName_Unit_Concrete_Test is Base_Test { - function gn(string memory lockupModel, string memory streamId) internal view returns (string memory) { - return nftDescriptorMock.generateName_(lockupModel, streamId); - } - - function dyn(string memory streamId) internal pure returns (string memory) { - return string.concat("Sablier Lockup Dynamic #", streamId); - } - - function lin(string memory streamId) internal pure returns (string memory) { - return string.concat("Sablier Lockup Linear #", streamId); - } - - function test_GenerateName_Empty() external view { - assertEq(gn("", ""), "Sablier #", "metadata name"); - assertEq(gn("A", ""), "Sablier A #", "metadata name"); - assertEq(gn("", "1"), "Sablier #1", "metadata name"); - } - - function test_GenerateName() external view { - assertEq(gn("Lockup Dynamic", "1"), dyn("1"), "metadata name"); - assertEq(gn("Lockup Dynamic", "42"), dyn("42"), "metadata name"); - assertEq(gn("Lockup Dynamic", "1337"), dyn("1337"), "metadata name"); - assertEq(gn("Lockup Dynamic", "1234567"), dyn("1234567"), "metadata name"); - assertEq(gn("Lockup Dynamic", "123456890"), dyn("123456890"), "metadata name"); - assertEq(gn("Lockup Linear", "1"), lin("1"), "metadata name"); - assertEq(gn("Lockup Linear", "42"), lin("42"), "metadata name"); - assertEq(gn("Lockup Linear", "1337"), lin("1337"), "metadata name"); - assertEq(gn("Lockup Linear", "1234567"), lin("1234567"), "metadata name"); - assertEq(gn("Lockup Linear", "123456890"), lin("123456890"), "metadata name"); - } -} diff --git a/tests/unit/concrete/nft-descriptor/generateSVG.t.sol b/tests/unit/concrete/nft-descriptor/generateSVG.t.sol index aa43e874c..16edefc3d 100644 --- a/tests/unit/concrete/nft-descriptor/generateSVG.t.sol +++ b/tests/unit/concrete/nft-descriptor/generateSVG.t.sol @@ -22,12 +22,11 @@ contract GenerateSVG_Unit_Concrete_Test is Base_Test { progress: "0%", progressNumerical: 0, lockupAddress: "0xf3a045dc986015be9ae43bb3462ae5981b0816e0", - lockupModel: "Lockup Linear", status: "Pending" }) ); string memory expectedSVG = - unicode'Progress0%StatusPendingAmount100Duration5 Days0xf3a045dc986015be9ae43bb3462ae5981b0816e0 • Sablier Lockup Linear0xf3a045dc986015be9ae43bb3462ae5981b0816e0 • Sablier Lockup Linear0x03a6a84cd762d9707a21605b548aaab891562aab • DAI0x03a6a84cd762d9707a21605b548aaab891562aab • DAI'; + unicode'Progress0%StatusPendingAmount100Duration5 Days0xf3a045dc986015be9ae43bb3462ae5981b0816e0 • Sablier Lockup0xf3a045dc986015be9ae43bb3462ae5981b0816e0 • Sablier Lockup0x03a6a84cd762d9707a21605b548aaab891562aab • DAI0x03a6a84cd762d9707a21605b548aaab891562aab • DAI'; assertEq(actualSVG, expectedSVG, "SVG mismatch"); } @@ -42,12 +41,11 @@ contract GenerateSVG_Unit_Concrete_Test is Base_Test { progress: "42.35%", progressNumerical: 4235, lockupAddress: "0xf3a045dc986015be9ae43bb3462ae5981b0816e0", - lockupModel: "Lockup Linear", status: "Streaming" }) ); string memory expectedSVG = - unicode'Progress42.35%StatusStreamingAmount≥ 1.23MDuration91 Days0xf3a045dc986015be9ae43bb3462ae5981b0816e0 • Sablier Lockup Linear0xf3a045dc986015be9ae43bb3462ae5981b0816e0 • Sablier Lockup Linear0x03a6a84cd762d9707a21605b548aaab891562aab • DAI0x03a6a84cd762d9707a21605b548aaab891562aab • DAI'; + unicode'Progress42.35%StatusStreamingAmount≥ 1.23MDuration91 Days0xf3a045dc986015be9ae43bb3462ae5981b0816e0 • Sablier Lockup0xf3a045dc986015be9ae43bb3462ae5981b0816e0 • Sablier Lockup0x03a6a84cd762d9707a21605b548aaab891562aab • DAI0x03a6a84cd762d9707a21605b548aaab891562aab • DAI'; assertEq(actualSVG, expectedSVG, "SVG mismatch"); } @@ -62,12 +60,11 @@ contract GenerateSVG_Unit_Concrete_Test is Base_Test { progress: "100%", progressNumerical: 100, lockupAddress: "0xf3a045dc986015be9ae43bb3462ae5981b0816e0", - lockupModel: "Lockup Linear", status: "Depleted" }) ); string memory expectedSVG = - unicode'Progress100%StatusDepletedAmount100Duration5 Days0xf3a045dc986015be9ae43bb3462ae5981b0816e0 • Sablier Lockup Linear0xf3a045dc986015be9ae43bb3462ae5981b0816e0 • Sablier Lockup Linear0x03a6a84cd762d9707a21605b548aaab891562aab • DAI0x03a6a84cd762d9707a21605b548aaab891562aab • DAI'; + unicode'Progress100%StatusDepletedAmount100Duration5 Days0xf3a045dc986015be9ae43bb3462ae5981b0816e0 • Sablier Lockup0xf3a045dc986015be9ae43bb3462ae5981b0816e0 • Sablier Lockup0x03a6a84cd762d9707a21605b548aaab891562aab • DAI0x03a6a84cd762d9707a21605b548aaab891562aab • DAI'; assertEq(actualSVG, expectedSVG, "SVG mismatch"); } }