From b3ea12221ddbd153e867fda294f6b9f04a47aa0d Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Thu, 12 Dec 2024 13:19:32 +0530 Subject: [PATCH] perf: optimize NFTDescriptor contract (#1113) * test: update token uri tests * test: remove tokenURI tests for each model * perf: optimize NFTDescriptor contract feat: remove backward compatibility Co-authored-by: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> * perf: remove no longer needed low level call --------- Co-authored-by: andreivladbrg Co-authored-by: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> --- script/GenerateSVG.s.sol | 1 - src/LockupNFTDescriptor.sol | 67 +---- src/libraries/NFTSVG.sol | 11 +- tests/fork/NFTDescriptor.t.sol | 282 ------------------ tests/integration/Integration.t.sol | 2 +- .../lockup-base/token-uri/tokenURI.t.sol | 58 ++++ .../token-uri/tokenURI.tree | 2 +- .../lockup-dynamic/token-uri/tokenURI.t.sol | 58 ---- .../lockup-dynamic/token-uri/tokenURI.tree | 8 - .../lockup-linear/token-uri/tokenURI.t.sol | 58 ---- .../lockup-tranched/token-uri/tokenURI.t.sol | 58 ---- .../lockup-tranched/token-uri/tokenURI.tree | 8 - .../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 +- 18 files changed, 85 insertions(+), 647 deletions(-) delete mode 100644 tests/fork/NFTDescriptor.t.sol create mode 100644 tests/integration/concrete/lockup-base/token-uri/tokenURI.t.sol rename tests/integration/concrete/{lockup-linear => lockup-base}/token-uri/tokenURI.tree (84%) delete mode 100644 tests/integration/concrete/lockup-dynamic/token-uri/tokenURI.t.sol delete mode 100644 tests/integration/concrete/lockup-dynamic/token-uri/tokenURI.tree delete mode 100644 tests/integration/concrete/lockup-linear/token-uri/tokenURI.t.sol delete mode 100644 tests/integration/concrete/lockup-tranched/token-uri/tokenURI.t.sol delete mode 100644 tests/integration/concrete/lockup-tranched/token-uri/tokenURI.tree 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..e8110f693 100644 --- a/src/LockupNFTDescriptor.sol +++ b/src/LockupNFTDescriptor.sol @@ -8,11 +8,10 @@ import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol"; 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"; + /* ██╗ ██████╗ ██████╗██╗ ██╗██╗ ██╗██████╗ ███╗ ██╗███████╗████████╗ @@ -47,16 +46,12 @@ contract LockupNFTDescriptor is ILockupNFTDescriptor { address token; string tokenSymbol; uint128 depositedAmount; - bool isTransferable; string json; ISablierLockup lockup; - string lockupModel; string lockupStringified; - bytes returnData; string status; string svg; uint256 streamedPercentage; - bool success; } /// @inheritdoc ILockupNFTDescriptor @@ -65,22 +60,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,18 +88,10 @@ contract LockupNFTDescriptor is ILockupNFTDescriptor { lockupAddress: vars.lockupStringified, progress: stringifyPercentage(vars.streamedPercentage), progressNumerical: vars.streamedPercentage, - status: vars.status, - lockupModel: vars.lockupModel + status: vars.status }) ); - // Performs a low-level call to handle older deployments that miss the `isTransferable` function. - (vars.success, vars.returnData) = - address(vars.lockup).staticcall(abi.encodeCall(ISablierLockupBase.isTransferable, (streamId))); - - // When the call has failed, the stream NFT is assumed to be transferable. - vars.isTransferable = vars.success ? abi.decode(vars.returnData, (bool)) : true; - // Generate the JSON metadata. vars.json = string.concat( '{"attributes":', @@ -125,15 +102,14 @@ contract LockupNFTDescriptor is ILockupNFTDescriptor { }), ',"description":"', generateDescription({ - lockupModel: vars.lockupModel, tokenSymbol: vars.tokenSymbol, lockupStringified: vars.lockupStringified, tokenAddress: vars.token.toHexString(), streamId: streamId.toString(), - isTransferable: vars.isTransferable + isTransferable: vars.lockup.isTransferable(streamId) }), '","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 +262,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 +279,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 +295,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 +318,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 new file mode 100644 index 000000000..7069afbcf --- /dev/null +++ b/tests/integration/concrete/lockup-base/token-uri/tokenURI.t.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: UNLICENSED +// solhint-disable max-line-length,no-console,quotes +pragma solidity >=0.8.22 <0.9.0; + +import { IERC721Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; +import { console2 } from "forge-std/src/console2.sol"; +import { StdStyle } from "forge-std/src/StdStyle.sol"; +import { Base64 } from "solady/src/utils/Base64.sol"; + +import { Integration_Test } from "../../../Integration.t.sol"; + +/// @dev Requirements for these tests to work: +/// - The stream ID must be 1 +/// - The stream's sender must be `0x6332e7b1deb1f1a0b77b2bb18b144330c7291bca`, i.e. `makeAddr("Sender")` +/// - The stream token must have the DAI symbol +/// - The contract deployer, i.e. the `sender` config option in `foundry.toml`, must have the default value +/// 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38 so that the deployed contracts have the same addresses as +/// the values hard coded in the tests below +contract TokenURI_Lockup_Integration_Concrete_Test is Integration_Test { + address internal constant LOCKUP = 0x923b5Ab3714FD343316aF5A5434582Fd16722523; + + /// @dev To make these tests noninvasive, they are run only when the contract address matches the hard coded value. + modifier skipOnMismatch() { + if (address(lockup) == LOCKUP) { + _; + } else { + console2.log(StdStyle.yellow('Warning: "Lockup.tokenURI" tests skipped due to address mismatch')); + } + } + + function test_RevertGiven_NFTNotExist() external { + vm.expectRevert(abi.encodeWithSelector(IERC721Errors.ERC721NonexistentToken.selector, nullStreamId)); + lockup.tokenURI({ tokenId: nullStreamId }); + } + + /// @dev If you need to update the hard-coded token URI: + /// 1. Use "vm.writeFile" to log the strings to a file. + /// 2. Remember to escape the EOL character \n with \\n. + function test_WhenTokenURIDecoded() external skipOnMismatch givenNFTExists { + vm.warp({ newTimestamp: defaults.START_TIME() + defaults.TOTAL_DURATION() / 4 }); + + string memory tokenURI = lockup.tokenURI(defaultStreamId); + 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":"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"); + } + + function test_WhenTokenURINotDecoded() external skipOnMismatch givenNFTExists { + vm.warp({ newTimestamp: defaults.START_TIME() + defaults.TOTAL_DURATION() / 4 }); + + string memory actualTokenURI = lockup.tokenURI(defaultStreamId); + string memory expectedTokenURI = + "data:application/json;base64,eyJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoiVG9rZW4iLCJ2YWx1ZSI6IkRBSSJ9LHsidHJhaXRfdHlwZSI6IlNlbmRlciIsInZhbHVlIjoiMHg2MzMyZTdiMWRlYjFmMWEwYjc3YjJiYjE4YjE0NDMzMGM3MjkxYmNhIn0seyJ0cmFpdF90eXBlIjoiU3RhdHVzIiwidmFsdWUiOiJTdHJlYW1pbmcifV0sImRlc2NyaXB0aW9uIjoiVGhpcyBORlQgcmVwcmVzZW50cyBhIHN0cmVhbSBpbiBTYWJsaWVyIExvY2t1cCBjb250cmFjdC4gVGhlIG93bmVyIG9mIHRoaXMgTkZUIGNhbiB3aXRoZHJhdyB0aGUgc3RyZWFtZWQgdG9rZW5zLCB3aGljaCBhcmUgZGVub21pbmF0ZWQgaW4gREFJLlxuXG4tIFN0cmVhbSBJRDogMVxuLSBTYWJsaWVyIExvY2t1cCBBZGRyZXNzOiAweDkyM2I1YWIzNzE0ZmQzNDMzMTZhZjVhNTQzNDU4MmZkMTY3MjI1MjNcbi0gREFJIEFkZHJlc3M6IDB4ZjYyODQ5ZjlhMGI1YmYyOTEzYjM5NjA5OGY3YzcwMTliNTFhODIwYVxuXG7imqDvuI8gV0FSTklORzogVHJhbnNmZXJyaW5nIHRoZSBORlQgbWFrZXMgdGhlIG5ldyBvd25lciB0aGUgcmVjaXBpZW50IG9mIHRoZSBzdHJlYW0uIFRoZSBmdW5kcyBhcmUgbm90IGF1dG9tYXRpY2FsbHkgd2l0aGRyYXduIGZvciB0aGUgcHJldmlvdXMgcmVjaXBpZW50LiIsImV4dGVybmFsX3VybCI6Imh0dHBzOi8vc2FibGllci5jb20iLCJuYW1lIjoiU2FibGllciBMb2NrdXAgIzEiLCJpbWFnZSI6ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJeE1EQXdJaUJvWldsbmFIUTlJakV3TURBaUlIWnBaWGRDYjNnOUlqQWdNQ0F4TURBd0lERXdNREFpUGp4eVpXTjBJSGRwWkhSb1BTSXhNREFsSWlCb1pXbG5hSFE5SWpFd01DVWlJR1pwYkhSbGNqMGlkWEpzS0NOT2IybHpaU2tpTHo0OGNtVmpkQ0I0UFNJM01DSWdlVDBpTnpBaUlIZHBaSFJvUFNJNE5qQWlJR2hsYVdkb2REMGlPRFl3SWlCbWFXeHNQU0lqWm1abUlpQm1hV3hzTFc5d1lXTnBkSGs5SWk0d015SWdjbmc5SWpRMUlpQnllVDBpTkRVaUlITjBjbTlyWlQwaUkyWm1aaUlnYzNSeWIydGxMVzl3WVdOcGRIazlJaTR4SWlCemRISnZhMlV0ZDJsa2RHZzlJalFpTHo0OFpHVm1jejQ4WTJseVkyeGxJR2xrUFNKSGJHOTNJaUJ5UFNJMU1EQWlJR1pwYkd3OUluVnliQ2dqVW1Ga2FXRnNSMnh2ZHlraUx6NDhabWxzZEdWeUlHbGtQU0pPYjJselpTSStQR1psUm14dmIyUWdlRDBpTUNJZ2VUMGlNQ0lnZDJsa2RHZzlJakV3TUNVaUlHaGxhV2RvZEQwaU1UQXdKU0lnWm14dmIyUXRZMjlzYjNJOUltaHpiQ2d5TXpBc01qRWxMREV4SlNraUlHWnNiMjlrTFc5d1lXTnBkSGs5SWpFaUlISmxjM1ZzZEQwaVpteHZiMlJHYVd4c0lpOCtQR1psVkhWeVluVnNaVzVqWlNCaVlYTmxSbkpsY1hWbGJtTjVQU0l1TkNJZ2JuVnRUMk4wWVhabGN6MGlNeUlnY21WemRXeDBQU0pPYjJselpTSWdkSGx3WlQwaVpuSmhZM1JoYkU1dmFYTmxJaTgrUEdabFFteGxibVFnYVc0OUlrNXZhWE5sSWlCcGJqSTlJbVpzYjI5a1JtbHNiQ0lnYlc5a1pUMGljMjltZEMxc2FXZG9kQ0l2UGp3dlptbHNkR1Z5UGp4d1lYUm9JR2xrUFNKTWIyZHZJaUJtYVd4c1BTSWpabVptSWlCbWFXeHNMVzl3WVdOcGRIazlJaTR4SWlCa1BTSnRNVE16TGpVMU9Td3hNalF1TURNMFl5MHVNREV6TERJdU5ERXlMVEV1TURVNUxEUXVPRFE0TFRJdU9USXpMRFl1TkRBeUxUSXVOVFU0TERFdU9ERTVMVFV1TVRZNExETXVORE01TFRjdU9EZzRMRFF1T1RrMkxURTBMalEwTERndU1qWXlMVE14TGpBME55d3hNaTQxTmpVdE5EY3VOamMwTERFeUxqVTJPUzA0TGpnMU9DNHdNell0TVRjdU9ETTRMVEV1TWpjeUxUSTJMak15T0MwekxqWTJNeTA1TGpnd05pMHlMamMyTmkweE9TNHdPRGN0Tnk0eE1UTXRNamN1TlRZeUxURXlMamMzT0MweE15NDROREl0T0M0d01qVXNPUzQwTmpndE1qZ3VOakEyTERFMkxqRTFNeTB6TlM0eU5qVm9NR015TGpBek5TMHhMamd6T0N3MExqSTFNaTB6TGpVME5pdzJMalEyTXkwMUxqSXlOR2d3WXpZdU5ESTVMVFV1TmpVMUxERTJMakl4T0MweUxqZ3pOU3d5TUM0ek5UZ3NOQzR4Tnl3MExqRTBNeXcxTGpBMU55dzRMamd4Tml3NUxqWTBPU3d4TXk0NU1pd3hNeTQzTXpSb0xqQXpOMk0xTGpjek5pdzJMalEyTVN3eE5TNHpOVGN0TWk0eU5UTXNPUzR6T0MwNExqUTRMREFzTUMwekxqVXhOUzB6TGpVeE5TMHpMalV4TlMwekxqVXhOUzB4TVM0ME9TMHhNUzQwTnpndE5USXVOalUyTFRVeUxqWTJOQzAyTkM0NE16Y3ROalF1T0RNM2JDNHdORGt0TGpBek4yTXRNUzQzTWpVdE1TNDJNRFl0TWk0M01Ua3RNeTQ0TkRjdE1pNDNOVEV0Tmk0eU1EUm9NR010TGpBME5pMHlMak0zTlN3eExqQTJNaTAwTGpVNE1pd3lMamN5TmkwMkxqSXlPV2d3YkM0eE9EVXRMakUwT0dnd1l5NHdPVGt0TGpBMk1pd3VNakl5TFM0eE5EZ3NMak0zTFM0eU5UbG9NR015TGpBMkxURXVNell5TERNdU9UVXhMVEl1TmpJeExEWXVNRFEwTFRNdU9EUXlRelUzTGpjMk15MHpMalEzTXl3NU55NDNOaTB5TGpNME1Td3hNamd1TmpNM0xERTRMak16TW1NeE5pNDJOekVzT1M0NU5EWXRNall1TXpRMExEVTBMamd4TXkwek9DNDJOVEVzTkRBdU1UazVMVFl1TWprNUxUWXVNRGsyTFRFNExqQTJNeTB4Tnk0M05ETXRNVGt1TmpZNExURTRMamd4TVMwMkxqQXhOaTAwTGpBME55MHhNeTR3TmpFc05DNDNOell0Tnk0M05USXNPUzQzTlRGc05qZ3VNalUwTERZNExqTTNNV014TGpjeU5Dd3hMall3TVN3eUxqY3hOQ3d6TGpnMExESXVOek00TERZdU1Ua3lXaUl2UGp4d1lYUm9JR2xrUFNKR2JHOWhkR2x1WjFSbGVIUWlJR1pwYkd3OUltNXZibVVpSUdROUlrMHhNalVnTkRWb056VXdjemd3SURBZ09EQWdPREIyTnpVd2N6QWdPREFnTFRnd0lEZ3dhQzAzTlRCekxUZ3dJREFnTFRnd0lDMDRNSFl0TnpVd2N6QWdMVGd3SURnd0lDMDRNQ0l2UGp4eVlXUnBZV3hIY21Ga2FXVnVkQ0JwWkQwaVVtRmthV0ZzUjJ4dmR5SStQSE4wYjNBZ2IyWm1jMlYwUFNJd0pTSWdjM1J2Y0MxamIyeHZjajBpYUhOc0tEWXpMRGsxSlN3MU55VXBJaUJ6ZEc5d0xXOXdZV05wZEhrOUlpNDJJaTgrUEhOMGIzQWdiMlptYzJWMFBTSXhNREFsSWlCemRHOXdMV052Ykc5eVBTSm9jMndvTWpNd0xESXhKU3d4TVNVcElpQnpkRzl3TFc5d1lXTnBkSGs5SWpBaUx6NDhMM0poWkdsaGJFZHlZV1JwWlc1MFBqeHNhVzVsWVhKSGNtRmthV1Z1ZENCcFpEMGlVMkZ1WkZSdmNDSWdlREU5SWpBbElpQjVNVDBpTUNVaVBqeHpkRzl3SUc5bVpuTmxkRDBpTUNVaUlITjBiM0F0WTI5c2IzSTlJbWh6YkNnMk15dzVOU1VzTlRjbEtTSXZQanh6ZEc5d0lHOW1abk5sZEQwaU1UQXdKU0lnYzNSdmNDMWpiMnh2Y2owaWFITnNLREl6TUN3eU1TVXNNVEVsS1NJdlBqd3ZiR2x1WldGeVIzSmhaR2xsYm5RK1BHeHBibVZoY2tkeVlXUnBaVzUwSUdsa1BTSlRZVzVrUW05MGRHOXRJaUI0TVQwaU1UQXdKU0lnZVRFOUlqRXdNQ1VpUGp4emRHOXdJRzltWm5ObGREMGlNVEFsSWlCemRHOXdMV052Ykc5eVBTSm9jMndvTWpNd0xESXhKU3d4TVNVcElpOCtQSE4wYjNBZ2IyWm1jMlYwUFNJeE1EQWxJaUJ6ZEc5d0xXTnZiRzl5UFNKb2Myd29Oak1zT1RVbExEVTNKU2tpTHo0OFlXNXBiV0YwWlNCaGRIUnlhV0oxZEdWT1lXMWxQU0o0TVNJZ1pIVnlQU0kyY3lJZ2NtVndaV0YwUTI5MWJuUTlJbWx1WkdWbWFXNXBkR1VpSUhaaGJIVmxjejBpTXpBbE96WXdKVHN4TWpBbE96WXdKVHN6TUNVN0lpOCtQQzlzYVc1bFlYSkhjbUZrYVdWdWRENDhiR2x1WldGeVIzSmhaR2xsYm5RZ2FXUTlJa2h2ZFhKbmJHRnpjMU4wY205clpTSWdaM0poWkdsbGJuUlVjbUZ1YzJadmNtMDlJbkp2ZEdGMFpTZzVNQ2tpSUdkeVlXUnBaVzUwVlc1cGRITTlJblZ6WlhKVGNHRmpaVTl1VlhObElqNDhjM1J2Y0NCdlptWnpaWFE5SWpVd0pTSWdjM1J2Y0MxamIyeHZjajBpYUhOc0tEWXpMRGsxSlN3MU55VXBJaTgrUEhOMGIzQWdiMlptYzJWMFBTSTRNQ1VpSUhOMGIzQXRZMjlzYjNJOUltaHpiQ2d5TXpBc01qRWxMREV4SlNraUx6NDhMMnhwYm1WaGNrZHlZV1JwWlc1MFBqeG5JR2xrUFNKSWIzVnlaMnhoYzNNaVBqeHdZWFJvSUdROUlrMGdOVEFzTXpZd0lHRWdNekF3TERNd01DQXdJREVzTVNBMk1EQXNNQ0JoSURNd01Dd3pNREFnTUNBeExERWdMVFl3TUN3d0lpQm1hV3hzUFNJalptWm1JaUJtYVd4c0xXOXdZV05wZEhrOUlpNHdNaUlnYzNSeWIydGxQU0oxY213b0kwaHZkWEpuYkdGemMxTjBjbTlyWlNraUlITjBjbTlyWlMxM2FXUjBhRDBpTkNJdlBqeHdZWFJvSUdROUltMDFOallzTVRZeExqSXdNWFl0TlRNdU9USTBZekF0TVRrdU16Z3lMVEl5TGpVeE15MHpOeTQxTmpNdE5qTXVNems0TFRVeExqRTVPQzAwTUM0M05UWXRNVE11TlRreUxUazBMamswTmkweU1TNHdOemt0TVRVeUxqVTROeTB5TVM0d056bHpMVEV4TVM0NE16Z3NOeTQwT0RjdE1UVXlMall3TWl3eU1TNHdOemxqTFRRd0xqZzVNeXd4TXk0Mk16WXROak11TkRFekxETXhMamd4TmkwMk15NDBNVE1zTlRFdU1UazRkalV6TGpreU5HTXdMREUzTGpFNE1Td3hOeTQzTURRc016TXVOREkzTERVd0xqSXlNeXcwTmk0ek9UUjJNamcwTGpnd09XTXRNekl1TlRFNUxERXlMamsyTFRVd0xqSXlNeXd5T1M0eU1EWXROVEF1TWpJekxEUTJMak01TkhZMU15NDVNalJqTUN3eE9TNHpPRElzTWpJdU5USXNNemN1TlRZekxEWXpMalF4TXl3MU1TNHhPVGdzTkRBdU56WXpMREV6TGpVNU1pdzVOQzQ1TlRRc01qRXVNRGM1TERFMU1pNDJNRElzTWpFdU1EYzVjekV4TVM0NE16RXROeTQwT0Rjc01UVXlMalU0TnkweU1TNHdOemxqTkRBdU9EZzJMVEV6TGpZek5pdzJNeTR6T1RndE16RXVPREUyTERZekxqTTVPQzAxTVM0eE9UaDJMVFV6TGpreU5HTXdMVEUzTGpFNU5pMHhOeTQzTURRdE16TXVORE0xTFRVd0xqSXlNeTAwTmk0ME1ERldNakEzTGpZd00yTXpNaTQxTVRrdE1USXVPVFkzTERVd0xqSXlNeTB5T1M0eU1EWXNOVEF1TWpJekxUUTJMalF3TVZwdExUTTBOeTQwTmpJc05UY3VOemt6YkRFek1DNDVOVGtzTVRNeExqQXlOeTB4TXpBdU9UVTVMREV6TVM0d01UTldNakU0TGprNU5GcHRNall5TGpreU5DNHdNakoyTWpZeUxqQXhPR3d0TVRNd0xqa3pOeTB4TXpFdU1EQTJMREV6TUM0NU16Y3RNVE14TGpBeE0xb2lJR1pwYkd3OUlpTXhOakU0TWpJaVBqd3ZjR0YwYUQ0OGNHOXNlV2R2YmlCd2IybHVkSE05SWpNMU1DQXpOVEF1TURJMklEUXhOUzR3TXlBeU9EUXVPVGM0SURJNE5TQXlPRFF1T1RjNElETTFNQ0F6TlRBdU1ESTJJaUJtYVd4c1BTSjFjbXdvSTFOaGJtUkNiM1IwYjIwcElpOCtQSEJoZEdnZ1pEMGliVFF4Tmk0ek5ERXNNamd4TGprM05XTXdMQzQ1TVRRdExqTTFOQ3d4TGpnd09TMHhMakF6TlN3eUxqWTRMVFV1TlRReUxEY3VNRGMyTFRNeUxqWTJNU3d4TWk0ME5TMDJOUzR5T0N3eE1pNDBOUzB6TWk0Mk1qUXNNQzAxT1M0M016Z3ROUzR6TnpRdE5qVXVNamd0TVRJdU5EVXRMalk0TVMwdU9EY3lMVEV1TURNMUxURXVOelkzTFRFdU1ETTFMVEl1Tmpnc01DMHVPVEUwTGpNMU5DMHhMamd3T0N3eExqQXpOUzB5TGpZM05pdzFMalUwTWkwM0xqQTNOaXd6TWk0Mk5UWXRNVEl1TkRVc05qVXVNamd0TVRJdU5EVXNNekl1TmpFNUxEQXNOVGt1TnpNNExEVXVNemMwTERZMUxqSTRMREV5TGpRMUxqWTRNUzQ0Tmpjc01TNHdNelVzTVM0M05qSXNNUzR3TXpVc01pNDJOelphSWlCbWFXeHNQU0oxY213b0kxTmhibVJVYjNBcElpOCtQSEJoZEdnZ1pEMGliVFE0TVM0ME5pdzFNRFF1TVRBeGRqVTRMalEwT1dNdE1pNHpOUzQzTnkwMExqZ3lMREV1TlRFdE55NHpPU3d5TGpJekxUTXdMak1zT0M0MU5DMDNOQzQyTlN3eE15NDVNaTB4TWpRdU1EWXNNVE11T1RJdE5UTXVOaXd3TFRFd01TNHlOQzAyTGpNekxURXpNUzQwTnkweE5pNHhObll0TlRndU5ETTVhREkyTWk0NU1sb2lJR1pwYkd3OUluVnliQ2dqVTJGdVpFSnZkSFJ2YlNraUx6NDhaV3hzYVhCelpTQmplRDBpTXpVd0lpQmplVDBpTlRBMExqRXdNU0lnY25nOUlqRXpNUzQwTmpJaUlISjVQU0l5T0M0eE1EZ2lJR1pwYkd3OUluVnliQ2dqVTJGdVpGUnZjQ2tpTHo0OFp5Qm1hV3hzUFNKdWIyNWxJaUJ6ZEhKdmEyVTlJblZ5YkNnalNHOTFjbWRzWVhOelUzUnliMnRsS1NJZ2MzUnliMnRsTFd4cGJtVmpZWEE5SW5KdmRXNWtJaUJ6ZEhKdmEyVXRiV2wwWlhKc2FXMXBkRDBpTVRBaUlITjBjbTlyWlMxM2FXUjBhRDBpTkNJK1BIQmhkR2dnWkQwaWJUVTJOUzQyTkRFc01UQTNMakk0WXpBc09TNDFNemN0TlM0MU5pd3hPQzQyTWprdE1UVXVOamMyTERJMkxqazNNMmd0TGpBeU0yTXRPUzR5TURRc055NDFPVFl0TWpJdU1UazBMREUwTGpVMk1pMHpPQzR4T1Rjc01qQXVOVGt5TFRNNUxqVXdOQ3d4TkM0NU16WXRPVGN1TXpJMUxESTBMak0xTlMweE5qRXVOek16TERJMExqTTFOUzA1TUM0ME9Dd3dMVEUyTnk0NU5EZ3RNVGd1TlRneUxURTVPUzQ1TlRNdE5EUXVPVFE0YUMwdU1ESXpZeTB4TUM0eE1UVXRPQzR6TkRRdE1UVXVOamMyTFRFM0xqUXpOeTB4TlM0Mk56WXRNall1T1RjekxEQXRNemt1TnpNMUxEazJMalUxTkMwM01TNDVNakVzTWpFMUxqWTFNaTAzTVM0NU1qRnpNakUxTGpZeU9Td3pNaTR4T0RVc01qRTFMall5T1N3M01TNDVNakZhSWk4K1BIQmhkR2dnWkQwaWJURXpOQzR6Tml3eE5qRXVNakF6WXpBc016a3VOek0xTERrMkxqVTFOQ3czTVM0NU1qRXNNakUxTGpZMU1pdzNNUzQ1TWpGek1qRTFMall5T1Mwek1pNHhPRFlzTWpFMUxqWXlPUzAzTVM0NU1qRWlMejQ4YkdsdVpTQjRNVDBpTVRNMExqTTJJaUI1TVQwaU1UWXhMakl3TXlJZ2VESTlJakV6TkM0ek5pSWdlVEk5SWpFd055NHlPQ0l2UGp4c2FXNWxJSGd4UFNJMU5qVXVOalFpSUhreFBTSXhOakV1TWpBeklpQjRNajBpTlRZMUxqWTBJaUI1TWowaU1UQTNMakk0SWk4K1BHeHBibVVnZURFOUlqRTROQzQxT0RRaUlIa3hQU0l5TURZdU9ESXpJaUI0TWowaU1UZzBMalU0TlNJZ2VUSTlJalV6Tnk0MU56a2lMejQ4YkdsdVpTQjRNVDBpTWpFNExqRTRNU0lnZVRFOUlqSXhPQzR4TVRnaUlIZ3lQU0l5TVRndU1UZ3hJaUI1TWowaU5UWXlMalV6TnlJdlBqeHNhVzVsSUhneFBTSTBPREV1T0RFNElpQjVNVDBpTWpFNExqRTBNaUlnZURJOUlqUTRNUzQ0TVRraUlIa3lQU0kxTmpJdU5ESTRJaTgrUEd4cGJtVWdlREU5SWpVeE5TNDBNVFVpSUhreFBTSXlNRGN1TXpVeUlpQjRNajBpTlRFMUxqUXhOaUlnZVRJOUlqVXpOeTQxTnpraUx6NDhjR0YwYUNCa1BTSnRNVGcwTGpVNExEVXpOeTQxT0dNd0xEVXVORFVzTkM0eU55d3hNQzQyTlN3eE1pNHdNeXd4TlM0ME1tZ3VNREpqTlM0MU1Td3pMak01TERFeUxqYzVMRFl1TlRVc01qRXVOVFVzT1M0ME1pd3pNQzR5TVN3NUxqa3NOemd1TURJc01UWXVNamdzTVRNeExqZ3pMREUyTGpJNExEUTVMalF4TERBc09UTXVOell0TlM0ek9Dd3hNalF1TURZdE1UTXVPVElzTWk0M0xTNDNOaXcxTGpJNUxURXVOVFFzTnk0M05TMHlMak0xTERndU56Y3RNaTQ0Tnl3eE5pNHdOUzAyTGpBMExESXhMalUyTFRrdU5ETm9NR00zTGpjMkxUUXVOemNzTVRJdU1EUXRPUzQ1Tnl3eE1pNHdOQzB4TlM0ME1pSXZQanh3WVhSb0lHUTlJbTB4T0RRdU5UZ3lMRFE1TWk0Mk5UWmpMVE14TGpNMU5Dd3hNaTQwT0RVdE5UQXVNakl6TERJNExqVTRMVFV3TGpJeU15dzBOaTR4TkRJc01DdzVMalV6Tml3MUxqVTJOQ3d4T0M0Mk1qY3NNVFV1TmpjM0xESTJMamsyT1dndU1ESXlZemd1TlRBekxEY3VNREExTERJd0xqSXhNeXd4TXk0ME5qTXNNelF1TlRJMExERTVMakUxT1N3NUxqazVPU3d6TGprNU1Td3lNUzR5Tmprc055NDJNRGtzTXpNdU5UazNMREV3TGpjNE9Dd3pOaTQwTlN3NUxqUXdOeXc0TWk0eE9ERXNNVFV1TURBeUxERXpNUzQ0TXpVc01UVXVNREF5Y3prMUxqTTJNeTAxTGpVNU5Td3hNekV1T0RBM0xURTFMakF3TW1NeE1DNDRORGN0TWk0M09Td3lNQzQ0TmpjdE5TNDVNallzTWprdU9USTBMVGt1TXpRNUxERXVNalEwTFM0ME5qY3NNaTQwTnpNdExqazBNaXd6TGpZM015MHhMalF5TkN3eE5DNHpNall0TlM0Mk9UWXNNall1TURNMUxURXlMakUyTVN3ek5DNDFNalF0TVRrdU1UY3phQzR3TWpKak1UQXVNVEUwTFRndU16UXlMREUxTGpZM055MHhOeTQwTXpNc01UVXVOamMzTFRJMkxqazJPU3d3TFRFM0xqVTJNaTB4T0M0NE5qa3RNek11TmpZMUxUVXdMakl5TXkwME5pNHhOU0l2UGp4d1lYUm9JR1E5SW0weE16UXVNellzTlRreUxqY3lZekFzTXprdU56TTFMRGsyTGpVMU5DdzNNUzQ1TWpFc01qRTFMalkxTWl3M01TNDVNakZ6TWpFMUxqWXlPUzB6TWk0eE9EWXNNakUxTGpZeU9TMDNNUzQ1TWpFaUx6NDhiR2x1WlNCNE1UMGlNVE0wTGpNMklpQjVNVDBpTlRreUxqY3lJaUI0TWowaU1UTTBMak0ySWlCNU1qMGlOVE00TGpjNU55SXZQanhzYVc1bElIZ3hQU0kxTmpVdU5qUWlJSGt4UFNJMU9USXVOeklpSUhneVBTSTFOalV1TmpRaUlIa3lQU0kxTXpndU56azNJaTgrUEhCdmJIbHNhVzVsSUhCdmFXNTBjejBpTkRneExqZ3lNaUEwT0RFdU9UQXhJRFE0TVM0M09UZ2dORGd4TGpnM055QTBPREV1TnpjMUlEUTRNUzQ0TlRRZ016VXdMakF4TlNBek5UQXVNREkySURJeE9DNHhPRFVnTWpFNExqRXlPU0l2UGp4d2IyeDViR2x1WlNCd2IybHVkSE05SWpJeE9DNHhPRFVnTkRneExqa3dNU0F5TVRndU1qTXhJRFE0TVM0NE5UUWdNelV3TGpBeE5TQXpOVEF1TURJMklEUTRNUzQ0TWpJZ01qRTRMakUxTWlJdlBqd3ZaejQ4TDJjK1BHY2dhV1E5SWxCeWIyZHlaWE56SWlCbWFXeHNQU0lqWm1abUlqNDhjbVZqZENCM2FXUjBhRDBpTWpBNElpQm9aV2xuYUhROUlqRXdNQ0lnWm1sc2JDMXZjR0ZqYVhSNVBTSXVNRE1pSUhKNFBTSXhOU0lnY25rOUlqRTFJaUJ6ZEhKdmEyVTlJaU5tWm1ZaUlITjBjbTlyWlMxdmNHRmphWFI1UFNJdU1TSWdjM1J5YjJ0bExYZHBaSFJvUFNJMElpOCtQSFJsZUhRZ2VEMGlNakFpSUhrOUlqTTBJaUJtYjI1MExXWmhiV2xzZVQwaUowTnZkWEpwWlhJZ1RtVjNKeXhCY21saGJDeHRiMjV2YzNCaFkyVWlJR1p2Ym5RdGMybDZaVDBpTWpKd2VDSStVSEp2WjNKbGMzTThMM1JsZUhRK1BIUmxlSFFnZUQwaU1qQWlJSGs5SWpjeUlpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5eEJjbWxoYkN4dGIyNXZjM0JoWTJVaUlHWnZiblF0YzJsNlpUMGlNalp3ZUNJK01qSXVPVGdsUEM5MFpYaDBQanhuSUdacGJHdzlJbTV2Ym1VaVBqeGphWEpqYkdVZ1kzZzlJakUyTmlJZ1kzazlJalV3SWlCeVBTSXlNaUlnYzNSeWIydGxQU0pvYzJ3b01qTXdMREl4SlN3eE1TVXBJaUJ6ZEhKdmEyVXRkMmxrZEdnOUlqRXdJaTgrUEdOcGNtTnNaU0JqZUQwaU1UWTJJaUJqZVQwaU5UQWlJSEJoZEdoTVpXNW5kR2c5SWpFd01EQXdJaUJ5UFNJeU1pSWdjM1J5YjJ0bFBTSm9jMndvTmpNc09UVWxMRFUzSlNraUlITjBjbTlyWlMxa1lYTm9ZWEp5WVhrOUlqRXdNREF3SWlCemRISnZhMlV0WkdGemFHOW1abk5sZEQwaU56Y3dNaUlnYzNSeWIydGxMV3hwYm1WallYQTlJbkp2ZFc1a0lpQnpkSEp2YTJVdGQybGtkR2c5SWpVaUlIUnlZVzV6Wm05eWJUMGljbTkwWVhSbEtDMDVNQ2tpSUhSeVlXNXpabTl5YlMxdmNtbG5hVzQ5SWpFMk5pQTFNQ0l2UGp3dlp6NDhMMmMrUEdjZ2FXUTlJbE4wWVhSMWN5SWdabWxzYkQwaUkyWm1aaUkrUEhKbFkzUWdkMmxrZEdnOUlqRTROQ0lnYUdWcFoyaDBQU0l4TURBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpBeklpQnllRDBpTVRVaUlISjVQU0l4TlNJZ2MzUnliMnRsUFNJalptWm1JaUJ6ZEhKdmEyVXRiM0JoWTJsMGVUMGlMakVpSUhOMGNtOXJaUzEzYVdSMGFEMGlOQ0l2UGp4MFpYaDBJSGc5SWpJd0lpQjVQU0l6TkNJZ1ptOXVkQzFtWVcxcGJIazlJaWREYjNWeWFXVnlJRTVsZHljc1FYSnBZV3dzYlc5dWIzTndZV05sSWlCbWIyNTBMWE5wZW1VOUlqSXljSGdpUGxOMFlYUjFjend2ZEdWNGRENDhkR1Y0ZENCNFBTSXlNQ0lnZVQwaU56SWlJR1p2Ym5RdFptRnRhV3g1UFNJblEyOTFjbWxsY2lCT1pYY25MRUZ5YVdGc0xHMXZibTl6Y0dGalpTSWdabTl1ZEMxemFYcGxQU0l5Tm5CNElqNVRkSEpsWVcxcGJtYzhMM1JsZUhRK1BDOW5QanhuSUdsa1BTSkJiVzkxYm5RaUlHWnBiR3c5SWlObVptWWlQanh5WldOMElIZHBaSFJvUFNJeE1qQWlJR2hsYVdkb2REMGlNVEF3SWlCbWFXeHNMVzl3WVdOcGRIazlJaTR3TXlJZ2NuZzlJakUxSWlCeWVUMGlNVFVpSUhOMGNtOXJaVDBpSTJabVppSWdjM1J5YjJ0bExXOXdZV05wZEhrOUlpNHhJaUJ6ZEhKdmEyVXRkMmxrZEdnOUlqUWlMejQ4ZEdWNGRDQjRQU0l5TUNJZ2VUMGlNelFpSUdadmJuUXRabUZ0YVd4NVBTSW5RMjkxY21sbGNpQk9aWGNuTEVGeWFXRnNMRzF2Ym05emNHRmpaU0lnWm05dWRDMXphWHBsUFNJeU1uQjRJajVCYlc5MWJuUThMM1JsZUhRK1BIUmxlSFFnZUQwaU1qQWlJSGs5SWpjeUlpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5eEJjbWxoYkN4dGIyNXZjM0JoWTJVaUlHWnZiblF0YzJsNlpUMGlNalp3ZUNJK0ppTTRPREExT3lBeE1FczhMM1JsZUhRK1BDOW5QanhuSUdsa1BTSkVkWEpoZEdsdmJpSWdabWxzYkQwaUkyWm1aaUkrUEhKbFkzUWdkMmxrZEdnOUlqRTFNaUlnYUdWcFoyaDBQU0l4TURBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpBeklpQnllRDBpTVRVaUlISjVQU0l4TlNJZ2MzUnliMnRsUFNJalptWm1JaUJ6ZEhKdmEyVXRiM0JoWTJsMGVUMGlMakVpSUhOMGNtOXJaUzEzYVdSMGFEMGlOQ0l2UGp4MFpYaDBJSGc5SWpJd0lpQjVQU0l6TkNJZ1ptOXVkQzFtWVcxcGJIazlJaWREYjNWeWFXVnlJRTVsZHljc1FYSnBZV3dzYlc5dWIzTndZV05sSWlCbWIyNTBMWE5wZW1VOUlqSXljSGdpUGtSMWNtRjBhVzl1UEM5MFpYaDBQangwWlhoMElIZzlJakl3SWlCNVBTSTNNaUlnWm05dWRDMW1ZVzFwYkhrOUlpZERiM1Z5YVdWeUlFNWxkeWNzUVhKcFlXd3NiVzl1YjNOd1lXTmxJaUJtYjI1MExYTnBlbVU5SWpJMmNIZ2lQaVpzZERzZ01TQkVZWGs4TDNSbGVIUStQQzluUGp3dlpHVm1jejQ4ZEdWNGRDQjBaWGgwTFhKbGJtUmxjbWx1WnowaWIzQjBhVzFwZW1WVGNHVmxaQ0krUEhSbGVIUlFZWFJvSUhOMFlYSjBUMlptYzJWMFBTSXRNVEF3SlNJZ2FISmxaajBpSTBac2IyRjBhVzVuVkdWNGRDSWdabWxzYkQwaUkyWm1aaUlnWm05dWRDMW1ZVzFwYkhrOUlpZERiM1Z5YVdWeUlFNWxkeWNzUVhKcFlXd3NiVzl1YjNOd1lXTmxJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDRJaUJtYjI1MExYTnBlbVU5SWpJMmNIZ2lQanhoYm1sdFlYUmxJR0ZrWkdsMGFYWmxQU0p6ZFcwaUlHRjBkSEpwWW5WMFpVNWhiV1U5SW5OMFlYSjBUMlptYzJWMElpQmlaV2RwYmowaU1ITWlJR1IxY2owaU5UQnpJaUJtY205dFBTSXdKU0lnY21Wd1pXRjBRMjkxYm5ROUltbHVaR1ZtYVc1cGRHVWlJSFJ2UFNJeE1EQWxJaTgrTUhnNU1qTmlOV0ZpTXpjeE5HWmtNelF6TXpFMllXWTFZVFUwTXpRMU9ESm1aREUyTnpJeU5USXpJT0tBb2lCVFlXSnNhV1Z5SUV4dlkydDFjRHd2ZEdWNGRGQmhkR2crUEhSbGVIUlFZWFJvSUhOMFlYSjBUMlptYzJWMFBTSXdKU0lnYUhKbFpqMGlJMFpzYjJGMGFXNW5WR1Y0ZENJZ1ptbHNiRDBpSTJabVppSWdabTl1ZEMxbVlXMXBiSGs5SWlkRGIzVnlhV1Z5SUU1bGR5Y3NRWEpwWVd3c2JXOXViM053WVdObElpQm1hV3hzTFc5d1lXTnBkSGs5SWk0NElpQm1iMjUwTFhOcGVtVTlJakkyY0hnaVBqeGhibWx0WVhSbElHRmtaR2wwYVhabFBTSnpkVzBpSUdGMGRISnBZblYwWlU1aGJXVTlJbk4wWVhKMFQyWm1jMlYwSWlCaVpXZHBiajBpTUhNaUlHUjFjajBpTlRCeklpQm1jbTl0UFNJd0pTSWdjbVZ3WldGMFEyOTFiblE5SW1sdVpHVm1hVzVwZEdVaUlIUnZQU0l4TURBbElpOCtNSGc1TWpOaU5XRmlNemN4Tkdaa016UXpNekUyWVdZMVlUVTBNelExT0RKbVpERTJOekl5TlRJeklPS0FvaUJUWVdKc2FXVnlJRXh2WTJ0MWNEd3ZkR1Y0ZEZCaGRHZytQSFJsZUhSUVlYUm9JSE4wWVhKMFQyWm1jMlYwUFNJdE5UQWxJaUJvY21WbVBTSWpSbXh2WVhScGJtZFVaWGgwSWlCbWFXeHNQU0lqWm1abUlpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5eEJjbWxoYkN4dGIyNXZjM0JoWTJVaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpnaUlHWnZiblF0YzJsNlpUMGlNalp3ZUNJK1BHRnVhVzFoZEdVZ1lXUmthWFJwZG1VOUluTjFiU0lnWVhSMGNtbGlkWFJsVG1GdFpUMGljM1JoY25SUFptWnpaWFFpSUdKbFoybHVQU0l3Y3lJZ1pIVnlQU0kxTUhNaUlHWnliMjA5SWpBbElpQnlaWEJsWVhSRGIzVnVkRDBpYVc1a1pXWnBibWwwWlNJZ2RHODlJakV3TUNVaUx6NHdlR1kyTWpnME9XWTVZVEJpTldKbU1qa3hNMkl6T1RZd09UaG1OMk0zTURFNVlqVXhZVGd5TUdFZzRvQ2lJRVJCU1R3dmRHVjRkRkJoZEdnK1BIUmxlSFJRWVhSb0lITjBZWEowVDJabWMyVjBQU0kxTUNVaUlHaHlaV1k5SWlOR2JHOWhkR2x1WjFSbGVIUWlJR1pwYkd3OUlpTm1abVlpSUdadmJuUXRabUZ0YVd4NVBTSW5RMjkxY21sbGNpQk9aWGNuTEVGeWFXRnNMRzF2Ym05emNHRmpaU0lnWm1sc2JDMXZjR0ZqYVhSNVBTSXVPQ0lnWm05dWRDMXphWHBsUFNJeU5uQjRJajQ4WVc1cGJXRjBaU0JoWkdScGRHbDJaVDBpYzNWdElpQmhkSFJ5YVdKMWRHVk9ZVzFsUFNKemRHRnlkRTltWm5ObGRDSWdZbVZuYVc0OUlqQnpJaUJrZFhJOUlqVXdjeUlnWm5KdmJUMGlNQ1VpSUhKbGNHVmhkRU52ZFc1MFBTSnBibVJsWm1sdWFYUmxJaUIwYnowaU1UQXdKU0l2UGpCNFpqWXlPRFE1WmpsaE1HSTFZbVl5T1RFellqTTVOakE1T0dZM1l6Y3dNVGxpTlRGaE9ESXdZU0RpZ0tJZ1JFRkpQQzkwWlhoMFVHRjBhRDQ4TDNSbGVIUStQSFZ6WlNCb2NtVm1QU0lqUjJ4dmR5SWdabWxzYkMxdmNHRmphWFI1UFNJdU9TSXZQangxYzJVZ2FISmxaajBpSTBkc2IzY2lJSGc5SWpFd01EQWlJSGs5SWpFd01EQWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqa2lMejQ4ZFhObElHaHlaV1k5SWlOTWIyZHZJaUI0UFNJeE56QWlJSGs5SWpFM01DSWdkSEpoYm5ObWIzSnRQU0p6WTJGc1pTZ3VOaWtpTHo0OGRYTmxJR2h5WldZOUlpTkliM1Z5WjJ4aGMzTWlJSGc5SWpFMU1DSWdlVDBpT1RBaUlIUnlZVzV6Wm05eWJUMGljbTkwWVhSbEtERXdLU0lnZEhKaGJuTm1iM0p0TFc5eWFXZHBiajBpTlRBd0lEVXdNQ0l2UGp4MWMyVWdhSEpsWmowaUkxQnliMmR5WlhOeklpQjRQU0l4TkRRaUlIazlJamM1TUNJdlBqeDFjMlVnYUhKbFpqMGlJMU4wWVhSMWN5SWdlRDBpTXpZNElpQjVQU0kzT1RBaUx6NDhkWE5sSUdoeVpXWTlJaU5CYlc5MWJuUWlJSGc5SWpVMk9DSWdlVDBpTnprd0lpOCtQSFZ6WlNCb2NtVm1QU0lqUkhWeVlYUnBiMjRpSUhnOUlqY3dOQ0lnZVQwaU56a3dJaTgrUEM5emRtYysifQ=="; + assertEq(actualTokenURI, expectedTokenURI, "token URI"); + } +} diff --git a/tests/integration/concrete/lockup-linear/token-uri/tokenURI.tree b/tests/integration/concrete/lockup-base/token-uri/tokenURI.tree similarity index 84% rename from tests/integration/concrete/lockup-linear/token-uri/tokenURI.tree rename to tests/integration/concrete/lockup-base/token-uri/tokenURI.tree index dcab9a745..c8b5523fc 100644 --- a/tests/integration/concrete/lockup-linear/token-uri/tokenURI.tree +++ b/tests/integration/concrete/lockup-base/token-uri/tokenURI.tree @@ -1,4 +1,4 @@ -TokenURI_Lockup_Linear_Integration_Concrete_Test +TokenURI_Lockup_Integration_Concrete_Test ├── given NFT not exist │ └── it should revert └── given NFT exists diff --git a/tests/integration/concrete/lockup-dynamic/token-uri/tokenURI.t.sol b/tests/integration/concrete/lockup-dynamic/token-uri/tokenURI.t.sol deleted file mode 100644 index e92b1ac82..000000000 --- a/tests/integration/concrete/lockup-dynamic/token-uri/tokenURI.t.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -// solhint-disable max-line-length,no-console,quotes -pragma solidity >=0.8.22 <0.9.0; - -import { IERC721Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; -import { console2 } from "forge-std/src/console2.sol"; -import { StdStyle } from "forge-std/src/StdStyle.sol"; -import { Base64 } from "solady/src/utils/Base64.sol"; - -import { Integration_Test } from "tests/integration/Integration.t.sol"; - -/// @dev Requirements for these tests to work: -/// - The stream ID must be 1 -/// - The stream's sender must be `0x6332e7b1deb1f1a0b77b2bb18b144330c7291bca`, i.e. `makeAddr("Sender")` -/// - The stream token must have the DAI symbol -/// - The contract deployer, i.e. the `sender` config option in `foundry.toml`, must have the default value -/// 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38 so that the deployed contracts have the same addresses as -/// the values hard coded in the tests below -contract TokenURI_Lockup_Dynamic_Integration_Concrete_Test is Integration_Test { - address internal constant LOCKUP = 0xDB25A7b768311dE128BBDa7B8426c3f9C74f3240; - - /// @dev To make these tests noninvasive, they are run only when the contract address matches the hard coded value. - modifier skipOnMismatch() { - if (address(lockup) == LOCKUP) { - _; - } else { - console2.log(StdStyle.yellow('Warning: "LockupDynamic.tokenURI" tests skipped due to address mismatch')); - } - } - - function test_RevertGiven_NFTNotExist() external { - vm.expectRevert(abi.encodeWithSelector(IERC721Errors.ERC721NonexistentToken.selector, nullStreamId)); - lockup.tokenURI({ tokenId: nullStreamId }); - } - - /// @dev If you need to update the hard-coded token URI: - /// 1. Use "vm.writeFile" to log the strings to a file. - /// 2. Remember to escape the EOL character \n with \\n. - function test_WhenTokenURIDecoded() external skipOnMismatch givenNFTExists { - vm.warp({ newTimestamp: defaults.START_TIME() + defaults.TOTAL_DURATION() / 4 }); - - string memory tokenURI = lockup.tokenURI(defaultStreamId); - 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":"DAI"},{"trait_type":"Sender","value":"0x6332e7b1deb1f1a0b77b2bb18b144330c7291bca"},{"trait_type":"Status","value":"Streaming"}],"description":"This NFT represents a payment stream in a Sablier Lockup Dynamic contract. The owner of this NFT can withdraw the streamed tokens, which are denominated in DAI.\\n\\n- Stream ID: 1\\n- Lockup Dynamic Address: 0xdb25a7b768311de128bbda7b8426c3f9c74f3240\\n- DAI Address: 0x03a6a84cd762d9707a21605b548aaab891562aab\\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 Dynamic #1","image":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAwIiBoZWlnaHQ9IjEwMDAiIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbHRlcj0idXJsKCNOb2lzZSkiLz48cmVjdCB4PSI3MCIgeT0iNzAiIHdpZHRoPSI4NjAiIGhlaWdodD0iODYwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4wMyIgcng9IjQ1IiByeT0iNDUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLW9wYWNpdHk9Ii4xIiBzdHJva2Utd2lkdGg9IjQiLz48ZGVmcz48Y2lyY2xlIGlkPSJHbG93IiByPSI1MDAiIGZpbGw9InVybCgjUmFkaWFsR2xvdykiLz48ZmlsdGVyIGlkPSJOb2lzZSI+PGZlRmxvb2QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmxvb2QtY29sb3I9ImhzbCgyMzAsMjElLDExJSkiIGZsb29kLW9wYWNpdHk9IjEiIHJlc3VsdD0iZmxvb2RGaWxsIi8+PGZlVHVyYnVsZW5jZSBiYXNlRnJlcXVlbmN5PSIuNCIgbnVtT2N0YXZlcz0iMyIgcmVzdWx0PSJOb2lzZSIgdHlwZT0iZnJhY3RhbE5vaXNlIi8+PGZlQmxlbmQgaW49Ik5vaXNlIiBpbjI9ImZsb29kRmlsbCIgbW9kZT0ic29mdC1saWdodCIvPjwvZmlsdGVyPjxwYXRoIGlkPSJMb2dvIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4xIiBkPSJtMTMzLjU1OSwxMjQuMDM0Yy0uMDEzLDIuNDEyLTEuMDU5LDQuODQ4LTIuOTIzLDYuNDAyLTIuNTU4LDEuODE5LTUuMTY4LDMuNDM5LTcuODg4LDQuOTk2LTE0LjQ0LDguMjYyLTMxLjA0NywxMi41NjUtNDcuNjc0LDEyLjU2OS04Ljg1OC4wMzYtMTcuODM4LTEuMjcyLTI2LjMyOC0zLjY2My05LjgwNi0yLjc2Ni0xOS4wODctNy4xMTMtMjcuNTYyLTEyLjc3OC0xMy44NDItOC4wMjUsOS40NjgtMjguNjA2LDE2LjE1My0zNS4yNjVoMGMyLjAzNS0xLjgzOCw0LjI1Mi0zLjU0Niw2LjQ2My01LjIyNGgwYzYuNDI5LTUuNjU1LDE2LjIxOC0yLjgzNSwyMC4zNTgsNC4xNyw0LjE0Myw1LjA1Nyw4LjgxNiw5LjY0OSwxMy45MiwxMy43MzRoLjAzN2M1LjczNiw2LjQ2MSwxNS4zNTctMi4yNTMsOS4zOC04LjQ4LDAsMC0zLjUxNS0zLjUxNS0zLjUxNS0zLjUxNS0xMS40OS0xMS40NzgtNTIuNjU2LTUyLjY2NC02NC44MzctNjQuODM3bC4wNDktLjAzN2MtMS43MjUtMS42MDYtMi43MTktMy44NDctMi43NTEtNi4yMDRoMGMtLjA0Ni0yLjM3NSwxLjA2Mi00LjU4MiwyLjcyNi02LjIyOWgwbC4xODUtLjE0OGgwYy4wOTktLjA2MiwuMjIyLS4xNDgsLjM3LS4yNTloMGMyLjA2LTEuMzYyLDMuOTUxLTIuNjIxLDYuMDQ0LTMuODQyQzU3Ljc2My0zLjQ3Myw5Ny43Ni0yLjM0MSwxMjguNjM3LDE4LjMzMmMxNi42NzEsOS45NDYtMjYuMzQ0LDU0LjgxMy0zOC42NTEsNDAuMTk5LTYuMjk5LTYuMDk2LTE4LjA2My0xNy43NDMtMTkuNjY4LTE4LjgxMS02LjAxNi00LjA0Ny0xMy4wNjEsNC43NzYtNy43NTIsOS43NTFsNjguMjU0LDY4LjM3MWMxLjcyNCwxLjYwMSwyLjcxNCwzLjg0LDIuNzM4LDYuMTkyWiIvPjxwYXRoIGlkPSJGbG9hdGluZ1RleHQiIGZpbGw9Im5vbmUiIGQ9Ik0xMjUgNDVoNzUwczgwIDAgODAgODB2NzUwczAgODAgLTgwIDgwaC03NTBzLTgwIDAgLTgwIC04MHYtNzUwczAgLTgwIDgwIC04MCIvPjxyYWRpYWxHcmFkaWVudCBpZD0iUmFkaWFsR2xvdyI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iaHNsKDYxLDg4JSw0MCUpIiBzdG9wLW9wYWNpdHk9Ii42Ii8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSJoc2woMjMwLDIxJSwxMSUpIiBzdG9wLW9wYWNpdHk9IjAiLz48L3JhZGlhbEdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iU2FuZFRvcCIgeDE9IjAlIiB5MT0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9ImhzbCg2MSw4OCUsNDAlKSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iaHNsKDIzMCwyMSUsMTElKSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJTYW5kQm90dG9tIiB4MT0iMTAwJSIgeTE9IjEwMCUiPjxzdG9wIG9mZnNldD0iMTAlIiBzdG9wLWNvbG9yPSJoc2woMjMwLDIxJSwxMSUpIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSJoc2woNjEsODglLDQwJSkiLz48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ4MSIgZHVyPSI2cyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHZhbHVlcz0iMzAlOzYwJTsxMjAlOzYwJTszMCU7Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9IkhvdXJnbGFzc1N0cm9rZSIgZ3JhZGllbnRUcmFuc2Zvcm09InJvdGF0ZSg5MCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iaHNsKDYxLDg4JSw0MCUpIi8+PHN0b3Agb2Zmc2V0PSI4MCUiIHN0b3AtY29sb3I9ImhzbCgyMzAsMjElLDExJSkiLz48L2xpbmVhckdyYWRpZW50PjxnIGlkPSJIb3VyZ2xhc3MiPjxwYXRoIGQ9Ik0gNTAsMzYwIGEgMzAwLDMwMCAwIDEsMSA2MDAsMCBhIDMwMCwzMDAgMCAxLDEgLTYwMCwwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4wMiIgc3Ryb2tlPSJ1cmwoI0hvdXJnbGFzc1N0cm9rZSkiIHN0cm9rZS13aWR0aD0iNCIvPjxwYXRoIGQ9Im01NjYsMTYxLjIwMXYtNTMuOTI0YzAtMTkuMzgyLTIyLjUxMy0zNy41NjMtNjMuMzk4LTUxLjE5OC00MC43NTYtMTMuNTkyLTk0Ljk0Ni0yMS4wNzktMTUyLjU4Ny0yMS4wNzlzLTExMS44MzgsNy40ODctMTUyLjYwMiwyMS4wNzljLTQwLjg5MywxMy42MzYtNjMuNDEzLDMxLjgxNi02My40MTMsNTEuMTk4djUzLjkyNGMwLDE3LjE4MSwxNy43MDQsMzMuNDI3LDUwLjIyMyw0Ni4zOTR2Mjg0LjgwOWMtMzIuNTE5LDEyLjk2LTUwLjIyMywyOS4yMDYtNTAuMjIzLDQ2LjM5NHY1My45MjRjMCwxOS4zODIsMjIuNTIsMzcuNTYzLDYzLjQxMyw1MS4xOTgsNDAuNzYzLDEzLjU5Miw5NC45NTQsMjEuMDc5LDE1Mi42MDIsMjEuMDc5czExMS44MzEtNy40ODcsMTUyLjU4Ny0yMS4wNzljNDAuODg2LTEzLjYzNiw2My4zOTgtMzEuODE2LDYzLjM5OC01MS4xOTh2LTUzLjkyNGMwLTE3LjE5Ni0xNy43MDQtMzMuNDM1LTUwLjIyMy00Ni40MDFWMjA3LjYwM2MzMi41MTktMTIuOTY3LDUwLjIyMy0yOS4yMDYsNTAuMjIzLTQ2LjQwMVptLTM0Ny40NjIsNTcuNzkzbDEzMC45NTksMTMxLjAyNy0xMzAuOTU5LDEzMS4wMTNWMjE4Ljk5NFptMjYyLjkyNC4wMjJ2MjYyLjAxOGwtMTMwLjkzNy0xMzEuMDA2LDEzMC45MzctMTMxLjAxM1oiIGZpbGw9IiMxNjE4MjIiPjwvcGF0aD48cG9seWdvbiBwb2ludHM9IjM1MCAzNTAuMDI2IDQxNS4wMyAyODQuOTc4IDI4NSAyODQuOTc4IDM1MCAzNTAuMDI2IiBmaWxsPSJ1cmwoI1NhbmRCb3R0b20pIi8+PHBhdGggZD0ibTQxNi4zNDEsMjgxLjk3NWMwLC45MTQtLjM1NCwxLjgwOS0xLjAzNSwyLjY4LTUuNTQyLDcuMDc2LTMyLjY2MSwxMi40NS02NS4yOCwxMi40NS0zMi42MjQsMC01OS43MzgtNS4zNzQtNjUuMjgtMTIuNDUtLjY4MS0uODcyLTEuMDM1LTEuNzY3LTEuMDM1LTIuNjgsMC0uOTE0LjM1NC0xLjgwOCwxLjAzNS0yLjY3Niw1LjU0Mi03LjA3NiwzMi42NTYtMTIuNDUsNjUuMjgtMTIuNDUsMzIuNjE5LDAsNTkuNzM4LDUuMzc0LDY1LjI4LDEyLjQ1LjY4MS44NjcsMS4wMzUsMS43NjIsMS4wMzUsMi42NzZaIiBmaWxsPSJ1cmwoI1NhbmRUb3ApIi8+PHBhdGggZD0ibTQ4MS40Niw1MDQuMTAxdjU4LjQ0OWMtMi4zNS43Ny00LjgyLDEuNTEtNy4zOSwyLjIzLTMwLjMsOC41NC03NC42NSwxMy45Mi0xMjQuMDYsMTMuOTItNTMuNiwwLTEwMS4yNC02LjMzLTEzMS40Ny0xNi4xNnYtNTguNDM5aDI2Mi45MloiIGZpbGw9InVybCgjU2FuZEJvdHRvbSkiLz48ZWxsaXBzZSBjeD0iMzUwIiBjeT0iNTA0LjEwMSIgcng9IjEzMS40NjIiIHJ5PSIyOC4xMDgiIGZpbGw9InVybCgjU2FuZFRvcCkiLz48ZyBmaWxsPSJub25lIiBzdHJva2U9InVybCgjSG91cmdsYXNzU3Ryb2tlKSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iNCI+PHBhdGggZD0ibTU2NS42NDEsMTA3LjI4YzAsOS41MzctNS41NiwxOC42MjktMTUuNjc2LDI2Ljk3M2gtLjAyM2MtOS4yMDQsNy41OTYtMjIuMTk0LDE0LjU2Mi0zOC4xOTcsMjAuNTkyLTM5LjUwNCwxNC45MzYtOTcuMzI1LDI0LjM1NS0xNjEuNzMzLDI0LjM1NS05MC40OCwwLTE2Ny45NDgtMTguNTgyLTE5OS45NTMtNDQuOTQ4aC0uMDIzYy0xMC4xMTUtOC4zNDQtMTUuNjc2LTE3LjQzNy0xNS42NzYtMjYuOTczLDAtMzkuNzM1LDk2LjU1NC03MS45MjEsMjE1LjY1Mi03MS45MjFzMjE1LjYyOSwzMi4xODUsMjE1LjYyOSw3MS45MjFaIi8+PHBhdGggZD0ibTEzNC4zNiwxNjEuMjAzYzAsMzkuNzM1LDk2LjU1NCw3MS45MjEsMjE1LjY1Miw3MS45MjFzMjE1LjYyOS0zMi4xODYsMjE1LjYyOS03MS45MjEiLz48bGluZSB4MT0iMTM0LjM2IiB5MT0iMTYxLjIwMyIgeDI9IjEzNC4zNiIgeTI9IjEwNy4yOCIvPjxsaW5lIHgxPSI1NjUuNjQiIHkxPSIxNjEuMjAzIiB4Mj0iNTY1LjY0IiB5Mj0iMTA3LjI4Ii8+PGxpbmUgeDE9IjE4NC41ODQiIHkxPSIyMDYuODIzIiB4Mj0iMTg0LjU4NSIgeTI9IjUzNy41NzkiLz48bGluZSB4MT0iMjE4LjE4MSIgeTE9IjIxOC4xMTgiIHgyPSIyMTguMTgxIiB5Mj0iNTYyLjUzNyIvPjxsaW5lIHgxPSI0ODEuODE4IiB5MT0iMjE4LjE0MiIgeDI9IjQ4MS44MTkiIHkyPSI1NjIuNDI4Ii8+PGxpbmUgeDE9IjUxNS40MTUiIHkxPSIyMDcuMzUyIiB4Mj0iNTE1LjQxNiIgeTI9IjUzNy41NzkiLz48cGF0aCBkPSJtMTg0LjU4LDUzNy41OGMwLDUuNDUsNC4yNywxMC42NSwxMi4wMywxNS40MmguMDJjNS41MSwzLjM5LDEyLjc5LDYuNTUsMjEuNTUsOS40MiwzMC4yMSw5LjksNzguMDIsMTYuMjgsMTMxLjgzLDE2LjI4LDQ5LjQxLDAsOTMuNzYtNS4zOCwxMjQuMDYtMTMuOTIsMi43LS43Niw1LjI5LTEuNTQsNy43NS0yLjM1LDguNzctMi44NywxNi4wNS02LjA0LDIxLjU2LTkuNDNoMGM3Ljc2LTQuNzcsMTIuMDQtOS45NywxMi4wNC0xNS40MiIvPjxwYXRoIGQ9Im0xODQuNTgyLDQ5Mi42NTZjLTMxLjM1NCwxMi40ODUtNTAuMjIzLDI4LjU4LTUwLjIyMyw0Ni4xNDIsMCw5LjUzNiw1LjU2NCwxOC42MjcsMTUuNjc3LDI2Ljk2OWguMDIyYzguNTAzLDcuMDA1LDIwLjIxMywxMy40NjMsMzQuNTI0LDE5LjE1OSw5Ljk5OSwzLjk5MSwyMS4yNjksNy42MDksMzMuNTk3LDEwLjc4OCwzNi40NSw5LjQwNyw4Mi4xODEsMTUuMDAyLDEzMS44MzUsMTUuMDAyczk1LjM2My01LjU5NSwxMzEuODA3LTE1LjAwMmMxMC44NDctMi43OSwyMC44NjctNS45MjYsMjkuOTI0LTkuMzQ5LDEuMjQ0LS40NjcsMi40NzMtLjk0MiwzLjY3My0xLjQyNCwxNC4zMjYtNS42OTYsMjYuMDM1LTEyLjE2MSwzNC41MjQtMTkuMTczaC4wMjJjMTAuMTE0LTguMzQyLDE1LjY3Ny0xNy40MzMsMTUuNjc3LTI2Ljk2OSwwLTE3LjU2Mi0xOC44NjktMzMuNjY1LTUwLjIyMy00Ni4xNSIvPjxwYXRoIGQ9Im0xMzQuMzYsNTkyLjcyYzAsMzkuNzM1LDk2LjU1NCw3MS45MjEsMjE1LjY1Miw3MS45MjFzMjE1LjYyOS0zMi4xODYsMjE1LjYyOS03MS45MjEiLz48bGluZSB4MT0iMTM0LjM2IiB5MT0iNTkyLjcyIiB4Mj0iMTM0LjM2IiB5Mj0iNTM4Ljc5NyIvPjxsaW5lIHgxPSI1NjUuNjQiIHkxPSI1OTIuNzIiIHgyPSI1NjUuNjQiIHkyPSI1MzguNzk3Ii8+PHBvbHlsaW5lIHBvaW50cz0iNDgxLjgyMiA0ODEuOTAxIDQ4MS43OTggNDgxLjg3NyA0ODEuNzc1IDQ4MS44NTQgMzUwLjAxNSAzNTAuMDI2IDIxOC4xODUgMjE4LjEyOSIvPjxwb2x5bGluZSBwb2ludHM9IjIxOC4xODUgNDgxLjkwMSAyMTguMjMxIDQ4MS44NTQgMzUwLjAxNSAzNTAuMDI2IDQ4MS44MjIgMjE4LjE1MiIvPjwvZz48L2c+PGcgaWQ9IlByb2dyZXNzIiBmaWxsPSIjZmZmIj48cmVjdCB3aWR0aD0iMjA4IiBoZWlnaHQ9IjEwMCIgZmlsbC1vcGFjaXR5PSIuMDMiIHJ4PSIxNSIgcnk9IjE1IiBzdHJva2U9IiNmZmYiIHN0cm9rZS1vcGFjaXR5PSIuMSIgc3Ryb2tlLXdpZHRoPSI0Ii8+PHRleHQgeD0iMjAiIHk9IjM0IiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjJweCI+UHJvZ3Jlc3M8L3RleHQ+PHRleHQgeD0iMjAiIHk9IjcyIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjZweCI+MjUlPC90ZXh0PjxnIGZpbGw9Im5vbmUiPjxjaXJjbGUgY3g9IjE2NiIgY3k9IjUwIiByPSIyMiIgc3Ryb2tlPSJoc2woMjMwLDIxJSwxMSUpIiBzdHJva2Utd2lkdGg9IjEwIi8+PGNpcmNsZSBjeD0iMTY2IiBjeT0iNTAiIHBhdGhMZW5ndGg9IjEwMDAwIiByPSIyMiIgc3Ryb2tlPSJoc2woNjEsODglLDQwJSkiIHN0cm9rZS1kYXNoYXJyYXk9IjEwMDAwIiBzdHJva2UtZGFzaG9mZnNldD0iNzUwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjUiIHRyYW5zZm9ybT0icm90YXRlKC05MCkiIHRyYW5zZm9ybS1vcmlnaW49IjE2NiA1MCIvPjwvZz48L2c+PGcgaWQ9IlN0YXR1cyIgZmlsbD0iI2ZmZiI+PHJlY3Qgd2lkdGg9IjE4NCIgaGVpZ2h0PSIxMDAiIGZpbGwtb3BhY2l0eT0iLjAzIiByeD0iMTUiIHJ5PSIxNSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZS13aWR0aD0iNCIvPjx0ZXh0IHg9IjIwIiB5PSIzNCIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjIycHgiPlN0YXR1czwvdGV4dD48dGV4dCB4PSIyMCIgeT0iNzIiIGZvbnQtZmFtaWx5PSInQ291cmllciBOZXcnLEFyaWFsLG1vbm9zcGFjZSIgZm9udC1zaXplPSIyNnB4Ij5TdHJlYW1pbmc8L3RleHQ+PC9nPjxnIGlkPSJBbW91bnQiIGZpbGw9IiNmZmYiPjxyZWN0IHdpZHRoPSIxMjAiIGhlaWdodD0iMTAwIiBmaWxsLW9wYWNpdHk9Ii4wMyIgcng9IjE1IiByeT0iMTUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLW9wYWNpdHk9Ii4xIiBzdHJva2Utd2lkdGg9IjQiLz48dGV4dCB4PSIyMCIgeT0iMzQiIGZvbnQtZmFtaWx5PSInQ291cmllciBOZXcnLEFyaWFsLG1vbm9zcGFjZSIgZm9udC1zaXplPSIyMnB4Ij5BbW91bnQ8L3RleHQ+PHRleHQgeD0iMjAiIHk9IjcyIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjZweCI+JiM4ODA1OyAxMEs8L3RleHQ+PC9nPjxnIGlkPSJEdXJhdGlvbiIgZmlsbD0iI2ZmZiI+PHJlY3Qgd2lkdGg9IjE1MiIgaGVpZ2h0PSIxMDAiIGZpbGwtb3BhY2l0eT0iLjAzIiByeD0iMTUiIHJ5PSIxNSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZS13aWR0aD0iNCIvPjx0ZXh0IHg9IjIwIiB5PSIzNCIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjIycHgiPkR1cmF0aW9uPC90ZXh0Pjx0ZXh0IHg9IjIwIiB5PSI3MiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjI2cHgiPiZsdDsgMSBEYXk8L3RleHQ+PC9nPjwvZGVmcz48dGV4dCB0ZXh0LXJlbmRlcmluZz0ib3B0aW1pemVTcGVlZCI+PHRleHRQYXRoIHN0YXJ0T2Zmc2V0PSItMTAwJSIgaHJlZj0iI0Zsb2F0aW5nVGV4dCIgZmlsbD0iI2ZmZiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmaWxsLW9wYWNpdHk9Ii44IiBmb250LXNpemU9IjI2cHgiPjxhbmltYXRlIGFkZGl0aXZlPSJzdW0iIGF0dHJpYnV0ZU5hbWU9InN0YXJ0T2Zmc2V0IiBiZWdpbj0iMHMiIGR1cj0iNTBzIiBmcm9tPSIwJSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHRvPSIxMDAlIi8+MHhkYjI1YTdiNzY4MzExZGUxMjhiYmRhN2I4NDI2YzNmOWM3NGYzMjQwIOKAoiBTYWJsaWVyIFYyIExvY2t1cCBEeW5hbWljPC90ZXh0UGF0aD48dGV4dFBhdGggc3RhcnRPZmZzZXQ9IjAlIiBocmVmPSIjRmxvYXRpbmdUZXh0IiBmaWxsPSIjZmZmIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZpbGwtb3BhY2l0eT0iLjgiIGZvbnQtc2l6ZT0iMjZweCI+PGFuaW1hdGUgYWRkaXRpdmU9InN1bSIgYXR0cmlidXRlTmFtZT0ic3RhcnRPZmZzZXQiIGJlZ2luPSIwcyIgZHVyPSI1MHMiIGZyb209IjAlIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgdG89IjEwMCUiLz4weGRiMjVhN2I3NjgzMTFkZTEyOGJiZGE3Yjg0MjZjM2Y5Yzc0ZjMyNDAg4oCiIFNhYmxpZXIgVjIgTG9ja3VwIER5bmFtaWM8L3RleHRQYXRoPjx0ZXh0UGF0aCBzdGFydE9mZnNldD0iLTUwJSIgaHJlZj0iI0Zsb2F0aW5nVGV4dCIgZmlsbD0iI2ZmZiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmaWxsLW9wYWNpdHk9Ii44IiBmb250LXNpemU9IjI2cHgiPjxhbmltYXRlIGFkZGl0aXZlPSJzdW0iIGF0dHJpYnV0ZU5hbWU9InN0YXJ0T2Zmc2V0IiBiZWdpbj0iMHMiIGR1cj0iNTBzIiBmcm9tPSIwJSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHRvPSIxMDAlIi8+MHgwM2E2YTg0Y2Q3NjJkOTcwN2EyMTYwNWI1NDhhYWFiODkxNTYyYWFiIOKAoiBEQUk8L3RleHRQYXRoPjx0ZXh0UGF0aCBzdGFydE9mZnNldD0iNTAlIiBocmVmPSIjRmxvYXRpbmdUZXh0IiBmaWxsPSIjZmZmIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZpbGwtb3BhY2l0eT0iLjgiIGZvbnQtc2l6ZT0iMjZweCI+PGFuaW1hdGUgYWRkaXRpdmU9InN1bSIgYXR0cmlidXRlTmFtZT0ic3RhcnRPZmZzZXQiIGJlZ2luPSIwcyIgZHVyPSI1MHMiIGZyb209IjAlIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgdG89IjEwMCUiLz4weDAzYTZhODRjZDc2MmQ5NzA3YTIxNjA1YjU0OGFhYWI4OTE1NjJhYWIg4oCiIERBSTwvdGV4dFBhdGg+PC90ZXh0Pjx1c2UgaHJlZj0iI0dsb3ciIGZpbGwtb3BhY2l0eT0iLjkiLz48dXNlIGhyZWY9IiNHbG93IiB4PSIxMDAwIiB5PSIxMDAwIiBmaWxsLW9wYWNpdHk9Ii45Ii8+PHVzZSBocmVmPSIjTG9nbyIgeD0iMTcwIiB5PSIxNzAiIHRyYW5zZm9ybT0ic2NhbGUoLjYpIi8+PHVzZSBocmVmPSIjSG91cmdsYXNzIiB4PSIxNTAiIHk9IjkwIiB0cmFuc2Zvcm09InJvdGF0ZSgxMCkiIHRyYW5zZm9ybS1vcmlnaW49IjUwMCA1MDAiLz48dXNlIGhyZWY9IiNQcm9ncmVzcyIgeD0iMTQ0IiB5PSI3OTAiLz48dXNlIGhyZWY9IiNTdGF0dXMiIHg9IjM2OCIgeT0iNzkwIi8+PHVzZSBocmVmPSIjQW1vdW50IiB4PSI1NjgiIHk9Ijc5MCIvPjx1c2UgaHJlZj0iI0R1cmF0aW9uIiB4PSI3MDQiIHk9Ijc5MCIvPjwvc3ZnPg=="}'; - assertEq(actualDecodedTokenURI, expectedDecodedTokenURI, "decoded token URI"); - } - - function test_WhenTokenURINotDecoded() external skipOnMismatch givenNFTExists { - vm.warp({ newTimestamp: defaults.START_TIME() + defaults.TOTAL_DURATION() / 4 }); - - string memory actualTokenURI = lockup.tokenURI(defaultStreamId); - string memory expectedTokenURI = - "data:application/json;base64,eyJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoiQXNzZXQiLCJ2YWx1ZSI6IkRBSSJ9LHsidHJhaXRfdHlwZSI6IlNlbmRlciIsInZhbHVlIjoiMHg2MzMyZTdiMWRlYjFmMWEwYjc3YjJiYjE4YjE0NDMzMGM3MjkxYmNhIn0seyJ0cmFpdF90eXBlIjoiU3RhdHVzIiwidmFsdWUiOiJTdHJlYW1pbmcifV0sImRlc2NyaXB0aW9uIjoiVGhpcyBORlQgcmVwcmVzZW50cyBhIHBheW1lbnQgc3RyZWFtIGluIGEgU2FibGllciBMb2NrdXAgU2FibGllciBMb2NrdXAgTGluZWFyIGNvbnRyYWN0LiBUaGUgb3duZXIgb2YgdGhpcyBORlQgY2FuIHdpdGhkcmF3IHRoZSBzdHJlYW1lZCBhc3NldHMsIHdoaWNoIGFyZSBkZW5vbWluYXRlZCBpbiBEQUkuXG5cbi0gU3RyZWFtIElEOiAxXG4tIFNhYmxpZXIgTG9ja3VwIExpbmVhciBBZGRyZXNzOiAweDcxYjk4MzdhZTFlMWFjOTk2NzZlNTJhMmJlODJhOTk4OTAwZTgyYzZcbi0gREFJIEFkZHJlc3M6IDB4NTYxNWRlYjc5OGJiM2U0ZGZhMDEzOWRmYTFiM2Q0MzNjYzIzYjcyZlxuXG7imqDvuI8gV0FSTklORzogVHJhbnNmZXJyaW5nIHRoZSBORlQgbWFrZXMgdGhlIG5ldyBvd25lciB0aGUgcmVjaXBpZW50IG9mIHRoZSBzdHJlYW0uIFRoZSBmdW5kcyBhcmUgbm90IGF1dG9tYXRpY2FsbHkgd2l0aGRyYXduIGZvciB0aGUgcHJldmlvdXMgcmVjaXBpZW50LiIsImV4dGVybmFsX3VybCI6Imh0dHBzOi8vc2FibGllci5jb20iLCJuYW1lIjoiU2FibGllciBTYWJsaWVyIExvY2t1cCBMaW5lYXIgIzEiLCJpbWFnZSI6ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJeE1EQXdJaUJvWldsbmFIUTlJakV3TURBaUlIWnBaWGRDYjNnOUlqQWdNQ0F4TURBd0lERXdNREFpUGp4eVpXTjBJSGRwWkhSb1BTSXhNREFsSWlCb1pXbG5hSFE5SWpFd01DVWlJR1pwYkhSbGNqMGlkWEpzS0NOT2IybHpaU2tpTHo0OGNtVmpkQ0I0UFNJM01DSWdlVDBpTnpBaUlIZHBaSFJvUFNJNE5qQWlJR2hsYVdkb2REMGlPRFl3SWlCbWFXeHNQU0lqWm1abUlpQm1hV3hzTFc5d1lXTnBkSGs5SWk0d015SWdjbmc5SWpRMUlpQnllVDBpTkRVaUlITjBjbTlyWlQwaUkyWm1aaUlnYzNSeWIydGxMVzl3WVdOcGRIazlJaTR4SWlCemRISnZhMlV0ZDJsa2RHZzlJalFpTHo0OFpHVm1jejQ4WTJseVkyeGxJR2xrUFNKSGJHOTNJaUJ5UFNJMU1EQWlJR1pwYkd3OUluVnliQ2dqVW1Ga2FXRnNSMnh2ZHlraUx6NDhabWxzZEdWeUlHbGtQU0pPYjJselpTSStQR1psUm14dmIyUWdlRDBpTUNJZ2VUMGlNQ0lnZDJsa2RHZzlJakV3TUNVaUlHaGxhV2RvZEQwaU1UQXdKU0lnWm14dmIyUXRZMjlzYjNJOUltaHpiQ2d5TXpBc01qRWxMREV4SlNraUlHWnNiMjlrTFc5d1lXTnBkSGs5SWpFaUlISmxjM1ZzZEQwaVpteHZiMlJHYVd4c0lpOCtQR1psVkhWeVluVnNaVzVqWlNCaVlYTmxSbkpsY1hWbGJtTjVQU0l1TkNJZ2JuVnRUMk4wWVhabGN6MGlNeUlnY21WemRXeDBQU0pPYjJselpTSWdkSGx3WlQwaVpuSmhZM1JoYkU1dmFYTmxJaTgrUEdabFFteGxibVFnYVc0OUlrNXZhWE5sSWlCcGJqSTlJbVpzYjI5a1JtbHNiQ0lnYlc5a1pUMGljMjltZEMxc2FXZG9kQ0l2UGp3dlptbHNkR1Z5UGp4d1lYUm9JR2xrUFNKTWIyZHZJaUJtYVd4c1BTSWpabVptSWlCbWFXeHNMVzl3WVdOcGRIazlJaTR4SWlCa1BTSnRNVE16TGpVMU9Td3hNalF1TURNMFl5MHVNREV6TERJdU5ERXlMVEV1TURVNUxEUXVPRFE0TFRJdU9USXpMRFl1TkRBeUxUSXVOVFU0TERFdU9ERTVMVFV1TVRZNExETXVORE01TFRjdU9EZzRMRFF1T1RrMkxURTBMalEwTERndU1qWXlMVE14TGpBME55d3hNaTQxTmpVdE5EY3VOamMwTERFeUxqVTJPUzA0TGpnMU9DNHdNell0TVRjdU9ETTRMVEV1TWpjeUxUSTJMak15T0MwekxqWTJNeTA1TGpnd05pMHlMamMyTmkweE9TNHdPRGN0Tnk0eE1UTXRNamN1TlRZeUxURXlMamMzT0MweE15NDROREl0T0M0d01qVXNPUzQwTmpndE1qZ3VOakEyTERFMkxqRTFNeTB6TlM0eU5qVm9NR015TGpBek5TMHhMamd6T0N3MExqSTFNaTB6TGpVME5pdzJMalEyTXkwMUxqSXlOR2d3WXpZdU5ESTVMVFV1TmpVMUxERTJMakl4T0MweUxqZ3pOU3d5TUM0ek5UZ3NOQzR4Tnl3MExqRTBNeXcxTGpBMU55dzRMamd4Tml3NUxqWTBPU3d4TXk0NU1pd3hNeTQzTXpSb0xqQXpOMk0xTGpjek5pdzJMalEyTVN3eE5TNHpOVGN0TWk0eU5UTXNPUzR6T0MwNExqUTRMREFzTUMwekxqVXhOUzB6TGpVeE5TMHpMalV4TlMwekxqVXhOUzB4TVM0ME9TMHhNUzQwTnpndE5USXVOalUyTFRVeUxqWTJOQzAyTkM0NE16Y3ROalF1T0RNM2JDNHdORGt0TGpBek4yTXRNUzQzTWpVdE1TNDJNRFl0TWk0M01Ua3RNeTQ0TkRjdE1pNDNOVEV0Tmk0eU1EUm9NR010TGpBME5pMHlMak0zTlN3eExqQTJNaTAwTGpVNE1pd3lMamN5TmkwMkxqSXlPV2d3YkM0eE9EVXRMakUwT0dnd1l5NHdPVGt0TGpBMk1pd3VNakl5TFM0eE5EZ3NMak0zTFM0eU5UbG9NR015TGpBMkxURXVNell5TERNdU9UVXhMVEl1TmpJeExEWXVNRFEwTFRNdU9EUXlRelUzTGpjMk15MHpMalEzTXl3NU55NDNOaTB5TGpNME1Td3hNamd1TmpNM0xERTRMak16TW1NeE5pNDJOekVzT1M0NU5EWXRNall1TXpRMExEVTBMamd4TXkwek9DNDJOVEVzTkRBdU1UazVMVFl1TWprNUxUWXVNRGsyTFRFNExqQTJNeTB4Tnk0M05ETXRNVGt1TmpZNExURTRMamd4TVMwMkxqQXhOaTAwTGpBME55MHhNeTR3TmpFc05DNDNOell0Tnk0M05USXNPUzQzTlRGc05qZ3VNalUwTERZNExqTTNNV014TGpjeU5Dd3hMall3TVN3eUxqY3hOQ3d6TGpnMExESXVOek00TERZdU1Ua3lXaUl2UGp4d1lYUm9JR2xrUFNKR2JHOWhkR2x1WjFSbGVIUWlJR1pwYkd3OUltNXZibVVpSUdROUlrMHhNalVnTkRWb056VXdjemd3SURBZ09EQWdPREIyTnpVd2N6QWdPREFnTFRnd0lEZ3dhQzAzTlRCekxUZ3dJREFnTFRnd0lDMDRNSFl0TnpVd2N6QWdMVGd3SURnd0lDMDRNQ0l2UGp4eVlXUnBZV3hIY21Ga2FXVnVkQ0JwWkQwaVVtRmthV0ZzUjJ4dmR5SStQSE4wYjNBZ2IyWm1jMlYwUFNJd0pTSWdjM1J2Y0MxamIyeHZjajBpYUhOc0tEVXpMRGM1SlN3ME1TVXBJaUJ6ZEc5d0xXOXdZV05wZEhrOUlpNDJJaTgrUEhOMGIzQWdiMlptYzJWMFBTSXhNREFsSWlCemRHOXdMV052Ykc5eVBTSm9jMndvTWpNd0xESXhKU3d4TVNVcElpQnpkRzl3TFc5d1lXTnBkSGs5SWpBaUx6NDhMM0poWkdsaGJFZHlZV1JwWlc1MFBqeHNhVzVsWVhKSGNtRmthV1Z1ZENCcFpEMGlVMkZ1WkZSdmNDSWdlREU5SWpBbElpQjVNVDBpTUNVaVBqeHpkRzl3SUc5bVpuTmxkRDBpTUNVaUlITjBiM0F0WTI5c2IzSTlJbWh6YkNnMU15dzNPU1VzTkRFbEtTSXZQanh6ZEc5d0lHOW1abk5sZEQwaU1UQXdKU0lnYzNSdmNDMWpiMnh2Y2owaWFITnNLREl6TUN3eU1TVXNNVEVsS1NJdlBqd3ZiR2x1WldGeVIzSmhaR2xsYm5RK1BHeHBibVZoY2tkeVlXUnBaVzUwSUdsa1BTSlRZVzVrUW05MGRHOXRJaUI0TVQwaU1UQXdKU0lnZVRFOUlqRXdNQ1VpUGp4emRHOXdJRzltWm5ObGREMGlNVEFsSWlCemRHOXdMV052Ykc5eVBTSm9jMndvTWpNd0xESXhKU3d4TVNVcElpOCtQSE4wYjNBZ2IyWm1jMlYwUFNJeE1EQWxJaUJ6ZEc5d0xXTnZiRzl5UFNKb2Myd29OVE1zTnprbExEUXhKU2tpTHo0OFlXNXBiV0YwWlNCaGRIUnlhV0oxZEdWT1lXMWxQU0o0TVNJZ1pIVnlQU0kyY3lJZ2NtVndaV0YwUTI5MWJuUTlJbWx1WkdWbWFXNXBkR1VpSUhaaGJIVmxjejBpTXpBbE96WXdKVHN4TWpBbE96WXdKVHN6TUNVN0lpOCtQQzlzYVc1bFlYSkhjbUZrYVdWdWRENDhiR2x1WldGeVIzSmhaR2xsYm5RZ2FXUTlJa2h2ZFhKbmJHRnpjMU4wY205clpTSWdaM0poWkdsbGJuUlVjbUZ1YzJadmNtMDlJbkp2ZEdGMFpTZzVNQ2tpSUdkeVlXUnBaVzUwVlc1cGRITTlJblZ6WlhKVGNHRmpaVTl1VlhObElqNDhjM1J2Y0NCdlptWnpaWFE5SWpVd0pTSWdjM1J2Y0MxamIyeHZjajBpYUhOc0tEVXpMRGM1SlN3ME1TVXBJaTgrUEhOMGIzQWdiMlptYzJWMFBTSTRNQ1VpSUhOMGIzQXRZMjlzYjNJOUltaHpiQ2d5TXpBc01qRWxMREV4SlNraUx6NDhMMnhwYm1WaGNrZHlZV1JwWlc1MFBqeG5JR2xrUFNKSWIzVnlaMnhoYzNNaVBqeHdZWFJvSUdROUlrMGdOVEFzTXpZd0lHRWdNekF3TERNd01DQXdJREVzTVNBMk1EQXNNQ0JoSURNd01Dd3pNREFnTUNBeExERWdMVFl3TUN3d0lpQm1hV3hzUFNJalptWm1JaUJtYVd4c0xXOXdZV05wZEhrOUlpNHdNaUlnYzNSeWIydGxQU0oxY213b0kwaHZkWEpuYkdGemMxTjBjbTlyWlNraUlITjBjbTlyWlMxM2FXUjBhRDBpTkNJdlBqeHdZWFJvSUdROUltMDFOallzTVRZeExqSXdNWFl0TlRNdU9USTBZekF0TVRrdU16Z3lMVEl5TGpVeE15MHpOeTQxTmpNdE5qTXVNems0TFRVeExqRTVPQzAwTUM0M05UWXRNVE11TlRreUxUazBMamswTmkweU1TNHdOemt0TVRVeUxqVTROeTB5TVM0d056bHpMVEV4TVM0NE16Z3NOeTQwT0RjdE1UVXlMall3TWl3eU1TNHdOemxqTFRRd0xqZzVNeXd4TXk0Mk16WXROak11TkRFekxETXhMamd4TmkwMk15NDBNVE1zTlRFdU1UazRkalV6TGpreU5HTXdMREUzTGpFNE1Td3hOeTQzTURRc016TXVOREkzTERVd0xqSXlNeXcwTmk0ek9UUjJNamcwTGpnd09XTXRNekl1TlRFNUxERXlMamsyTFRVd0xqSXlNeXd5T1M0eU1EWXROVEF1TWpJekxEUTJMak01TkhZMU15NDVNalJqTUN3eE9TNHpPRElzTWpJdU5USXNNemN1TlRZekxEWXpMalF4TXl3MU1TNHhPVGdzTkRBdU56WXpMREV6TGpVNU1pdzVOQzQ1TlRRc01qRXVNRGM1TERFMU1pNDJNRElzTWpFdU1EYzVjekV4TVM0NE16RXROeTQwT0Rjc01UVXlMalU0TnkweU1TNHdOemxqTkRBdU9EZzJMVEV6TGpZek5pdzJNeTR6T1RndE16RXVPREUyTERZekxqTTVPQzAxTVM0eE9UaDJMVFV6TGpreU5HTXdMVEUzTGpFNU5pMHhOeTQzTURRdE16TXVORE0xTFRVd0xqSXlNeTAwTmk0ME1ERldNakEzTGpZd00yTXpNaTQxTVRrdE1USXVPVFkzTERVd0xqSXlNeTB5T1M0eU1EWXNOVEF1TWpJekxUUTJMalF3TVZwdExUTTBOeTQwTmpJc05UY3VOemt6YkRFek1DNDVOVGtzTVRNeExqQXlOeTB4TXpBdU9UVTVMREV6TVM0d01UTldNakU0TGprNU5GcHRNall5TGpreU5DNHdNakoyTWpZeUxqQXhPR3d0TVRNd0xqa3pOeTB4TXpFdU1EQTJMREV6TUM0NU16Y3RNVE14TGpBeE0xb2lJR1pwYkd3OUlpTXhOakU0TWpJaVBqd3ZjR0YwYUQ0OGNHOXNlV2R2YmlCd2IybHVkSE05SWpNMU1DQXpOVEF1TURJMklEUXhOUzR3TXlBeU9EUXVPVGM0SURJNE5TQXlPRFF1T1RjNElETTFNQ0F6TlRBdU1ESTJJaUJtYVd4c1BTSjFjbXdvSTFOaGJtUkNiM1IwYjIwcElpOCtQSEJoZEdnZ1pEMGliVFF4Tmk0ek5ERXNNamd4TGprM05XTXdMQzQ1TVRRdExqTTFOQ3d4TGpnd09TMHhMakF6TlN3eUxqWTRMVFV1TlRReUxEY3VNRGMyTFRNeUxqWTJNU3d4TWk0ME5TMDJOUzR5T0N3eE1pNDBOUzB6TWk0Mk1qUXNNQzAxT1M0M016Z3ROUzR6TnpRdE5qVXVNamd0TVRJdU5EVXRMalk0TVMwdU9EY3lMVEV1TURNMUxURXVOelkzTFRFdU1ETTFMVEl1Tmpnc01DMHVPVEUwTGpNMU5DMHhMamd3T0N3eExqQXpOUzB5TGpZM05pdzFMalUwTWkwM0xqQTNOaXd6TWk0Mk5UWXRNVEl1TkRVc05qVXVNamd0TVRJdU5EVXNNekl1TmpFNUxEQXNOVGt1TnpNNExEVXVNemMwTERZMUxqSTRMREV5TGpRMUxqWTRNUzQ0Tmpjc01TNHdNelVzTVM0M05qSXNNUzR3TXpVc01pNDJOelphSWlCbWFXeHNQU0oxY213b0kxTmhibVJVYjNBcElpOCtQSEJoZEdnZ1pEMGliVFE0TVM0ME5pdzFNRFF1TVRBeGRqVTRMalEwT1dNdE1pNHpOUzQzTnkwMExqZ3lMREV1TlRFdE55NHpPU3d5TGpJekxUTXdMak1zT0M0MU5DMDNOQzQyTlN3eE15NDVNaTB4TWpRdU1EWXNNVE11T1RJdE5UTXVOaXd3TFRFd01TNHlOQzAyTGpNekxURXpNUzQwTnkweE5pNHhObll0TlRndU5ETTVhREkyTWk0NU1sb2lJR1pwYkd3OUluVnliQ2dqVTJGdVpFSnZkSFJ2YlNraUx6NDhaV3hzYVhCelpTQmplRDBpTXpVd0lpQmplVDBpTlRBMExqRXdNU0lnY25nOUlqRXpNUzQwTmpJaUlISjVQU0l5T0M0eE1EZ2lJR1pwYkd3OUluVnliQ2dqVTJGdVpGUnZjQ2tpTHo0OFp5Qm1hV3hzUFNKdWIyNWxJaUJ6ZEhKdmEyVTlJblZ5YkNnalNHOTFjbWRzWVhOelUzUnliMnRsS1NJZ2MzUnliMnRsTFd4cGJtVmpZWEE5SW5KdmRXNWtJaUJ6ZEhKdmEyVXRiV2wwWlhKc2FXMXBkRDBpTVRBaUlITjBjbTlyWlMxM2FXUjBhRDBpTkNJK1BIQmhkR2dnWkQwaWJUVTJOUzQyTkRFc01UQTNMakk0WXpBc09TNDFNemN0TlM0MU5pd3hPQzQyTWprdE1UVXVOamMyTERJMkxqazNNMmd0TGpBeU0yTXRPUzR5TURRc055NDFPVFl0TWpJdU1UazBMREUwTGpVMk1pMHpPQzR4T1Rjc01qQXVOVGt5TFRNNUxqVXdOQ3d4TkM0NU16WXRPVGN1TXpJMUxESTBMak0xTlMweE5qRXVOek16TERJMExqTTFOUzA1TUM0ME9Dd3dMVEUyTnk0NU5EZ3RNVGd1TlRneUxURTVPUzQ1TlRNdE5EUXVPVFE0YUMwdU1ESXpZeTB4TUM0eE1UVXRPQzR6TkRRdE1UVXVOamMyTFRFM0xqUXpOeTB4TlM0Mk56WXRNall1T1RjekxEQXRNemt1TnpNMUxEazJMalUxTkMwM01TNDVNakVzTWpFMUxqWTFNaTAzTVM0NU1qRnpNakUxTGpZeU9Td3pNaTR4T0RVc01qRTFMall5T1N3M01TNDVNakZhSWk4K1BIQmhkR2dnWkQwaWJURXpOQzR6Tml3eE5qRXVNakF6WXpBc016a3VOek0xTERrMkxqVTFOQ3czTVM0NU1qRXNNakUxTGpZMU1pdzNNUzQ1TWpGek1qRTFMall5T1Mwek1pNHhPRFlzTWpFMUxqWXlPUzAzTVM0NU1qRWlMejQ4YkdsdVpTQjRNVDBpTVRNMExqTTJJaUI1TVQwaU1UWXhMakl3TXlJZ2VESTlJakV6TkM0ek5pSWdlVEk5SWpFd055NHlPQ0l2UGp4c2FXNWxJSGd4UFNJMU5qVXVOalFpSUhreFBTSXhOakV1TWpBeklpQjRNajBpTlRZMUxqWTBJaUI1TWowaU1UQTNMakk0SWk4K1BHeHBibVVnZURFOUlqRTROQzQxT0RRaUlIa3hQU0l5TURZdU9ESXpJaUI0TWowaU1UZzBMalU0TlNJZ2VUSTlJalV6Tnk0MU56a2lMejQ4YkdsdVpTQjRNVDBpTWpFNExqRTRNU0lnZVRFOUlqSXhPQzR4TVRnaUlIZ3lQU0l5TVRndU1UZ3hJaUI1TWowaU5UWXlMalV6TnlJdlBqeHNhVzVsSUhneFBTSTBPREV1T0RFNElpQjVNVDBpTWpFNExqRTBNaUlnZURJOUlqUTRNUzQ0TVRraUlIa3lQU0kxTmpJdU5ESTRJaTgrUEd4cGJtVWdlREU5SWpVeE5TNDBNVFVpSUhreFBTSXlNRGN1TXpVeUlpQjRNajBpTlRFMUxqUXhOaUlnZVRJOUlqVXpOeTQxTnpraUx6NDhjR0YwYUNCa1BTSnRNVGcwTGpVNExEVXpOeTQxT0dNd0xEVXVORFVzTkM0eU55d3hNQzQyTlN3eE1pNHdNeXd4TlM0ME1tZ3VNREpqTlM0MU1Td3pMak01TERFeUxqYzVMRFl1TlRVc01qRXVOVFVzT1M0ME1pd3pNQzR5TVN3NUxqa3NOemd1TURJc01UWXVNamdzTVRNeExqZ3pMREUyTGpJNExEUTVMalF4TERBc09UTXVOell0TlM0ek9Dd3hNalF1TURZdE1UTXVPVElzTWk0M0xTNDNOaXcxTGpJNUxURXVOVFFzTnk0M05TMHlMak0xTERndU56Y3RNaTQ0Tnl3eE5pNHdOUzAyTGpBMExESXhMalUyTFRrdU5ETm9NR00zTGpjMkxUUXVOemNzTVRJdU1EUXRPUzQ1Tnl3eE1pNHdOQzB4TlM0ME1pSXZQanh3WVhSb0lHUTlJbTB4T0RRdU5UZ3lMRFE1TWk0Mk5UWmpMVE14TGpNMU5Dd3hNaTQwT0RVdE5UQXVNakl6TERJNExqVTRMVFV3TGpJeU15dzBOaTR4TkRJc01DdzVMalV6Tml3MUxqVTJOQ3d4T0M0Mk1qY3NNVFV1TmpjM0xESTJMamsyT1dndU1ESXlZemd1TlRBekxEY3VNREExTERJd0xqSXhNeXd4TXk0ME5qTXNNelF1TlRJMExERTVMakUxT1N3NUxqazVPU3d6TGprNU1Td3lNUzR5Tmprc055NDJNRGtzTXpNdU5UazNMREV3TGpjNE9Dd3pOaTQwTlN3NUxqUXdOeXc0TWk0eE9ERXNNVFV1TURBeUxERXpNUzQ0TXpVc01UVXVNREF5Y3prMUxqTTJNeTAxTGpVNU5Td3hNekV1T0RBM0xURTFMakF3TW1NeE1DNDRORGN0TWk0M09Td3lNQzQ0TmpjdE5TNDVNallzTWprdU9USTBMVGt1TXpRNUxERXVNalEwTFM0ME5qY3NNaTQwTnpNdExqazBNaXd6TGpZM015MHhMalF5TkN3eE5DNHpNall0TlM0Mk9UWXNNall1TURNMUxURXlMakUyTVN3ek5DNDFNalF0TVRrdU1UY3phQzR3TWpKak1UQXVNVEUwTFRndU16UXlMREUxTGpZM055MHhOeTQwTXpNc01UVXVOamMzTFRJMkxqazJPU3d3TFRFM0xqVTJNaTB4T0M0NE5qa3RNek11TmpZMUxUVXdMakl5TXkwME5pNHhOU0l2UGp4d1lYUm9JR1E5SW0weE16UXVNellzTlRreUxqY3lZekFzTXprdU56TTFMRGsyTGpVMU5DdzNNUzQ1TWpFc01qRTFMalkxTWl3M01TNDVNakZ6TWpFMUxqWXlPUzB6TWk0eE9EWXNNakUxTGpZeU9TMDNNUzQ1TWpFaUx6NDhiR2x1WlNCNE1UMGlNVE0wTGpNMklpQjVNVDBpTlRreUxqY3lJaUI0TWowaU1UTTBMak0ySWlCNU1qMGlOVE00TGpjNU55SXZQanhzYVc1bElIZ3hQU0kxTmpVdU5qUWlJSGt4UFNJMU9USXVOeklpSUhneVBTSTFOalV1TmpRaUlIa3lQU0kxTXpndU56azNJaTgrUEhCdmJIbHNhVzVsSUhCdmFXNTBjejBpTkRneExqZ3lNaUEwT0RFdU9UQXhJRFE0TVM0M09UZ2dORGd4TGpnM055QTBPREV1TnpjMUlEUTRNUzQ0TlRRZ016VXdMakF4TlNBek5UQXVNREkySURJeE9DNHhPRFVnTWpFNExqRXlPU0l2UGp4d2IyeDViR2x1WlNCd2IybHVkSE05SWpJeE9DNHhPRFVnTkRneExqa3dNU0F5TVRndU1qTXhJRFE0TVM0NE5UUWdNelV3TGpBeE5TQXpOVEF1TURJMklEUTRNUzQ0TWpJZ01qRTRMakUxTWlJdlBqd3ZaejQ4TDJjK1BHY2dhV1E5SWxCeWIyZHlaWE56SWlCbWFXeHNQU0lqWm1abUlqNDhjbVZqZENCM2FXUjBhRDBpTWpBNElpQm9aV2xuYUhROUlqRXdNQ0lnWm1sc2JDMXZjR0ZqYVhSNVBTSXVNRE1pSUhKNFBTSXhOU0lnY25rOUlqRTFJaUJ6ZEhKdmEyVTlJaU5tWm1ZaUlITjBjbTlyWlMxdmNHRmphWFI1UFNJdU1TSWdjM1J5YjJ0bExYZHBaSFJvUFNJMElpOCtQSFJsZUhRZ2VEMGlNakFpSUhrOUlqTTBJaUJtYjI1MExXWmhiV2xzZVQwaUowTnZkWEpwWlhJZ1RtVjNKeXhCY21saGJDeHRiMjV2YzNCaFkyVWlJR1p2Ym5RdGMybDZaVDBpTWpKd2VDSStVSEp2WjNKbGMzTThMM1JsZUhRK1BIUmxlSFFnZUQwaU1qQWlJSGs5SWpjeUlpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5eEJjbWxoYkN4dGIyNXZjM0JoWTJVaUlHWnZiblF0YzJsNlpUMGlNalp3ZUNJK01qVWxQQzkwWlhoMFBqeG5JR1pwYkd3OUltNXZibVVpUGp4amFYSmpiR1VnWTNnOUlqRTJOaUlnWTNrOUlqVXdJaUJ5UFNJeU1pSWdjM1J5YjJ0bFBTSm9jMndvTWpNd0xESXhKU3d4TVNVcElpQnpkSEp2YTJVdGQybGtkR2c5SWpFd0lpOCtQR05wY21Oc1pTQmplRDBpTVRZMklpQmplVDBpTlRBaUlIQmhkR2hNWlc1bmRHZzlJakV3TURBd0lpQnlQU0l5TWlJZ2MzUnliMnRsUFNKb2Myd29OVE1zTnprbExEUXhKU2tpSUhOMGNtOXJaUzFrWVhOb1lYSnlZWGs5SWpFd01EQXdJaUJ6ZEhKdmEyVXRaR0Z6YUc5bVpuTmxkRDBpTnpVd01DSWdjM1J5YjJ0bExXeHBibVZqWVhBOUluSnZkVzVrSWlCemRISnZhMlV0ZDJsa2RHZzlJalVpSUhSeVlXNXpabTl5YlQwaWNtOTBZWFJsS0MwNU1Da2lJSFJ5WVc1elptOXliUzF2Y21sbmFXNDlJakUyTmlBMU1DSXZQand2Wno0OEwyYytQR2NnYVdROUlsTjBZWFIxY3lJZ1ptbHNiRDBpSTJabVppSStQSEpsWTNRZ2QybGtkR2c5SWpFNE5DSWdhR1ZwWjJoMFBTSXhNREFpSUdacGJHd3RiM0JoWTJsMGVUMGlMakF6SWlCeWVEMGlNVFVpSUhKNVBTSXhOU0lnYzNSeWIydGxQU0lqWm1abUlpQnpkSEp2YTJVdGIzQmhZMmwwZVQwaUxqRWlJSE4wY205clpTMTNhV1IwYUQwaU5DSXZQangwWlhoMElIZzlJakl3SWlCNVBTSXpOQ0lnWm05dWRDMW1ZVzFwYkhrOUlpZERiM1Z5YVdWeUlFNWxkeWNzUVhKcFlXd3NiVzl1YjNOd1lXTmxJaUJtYjI1MExYTnBlbVU5SWpJeWNIZ2lQbE4wWVhSMWN6d3ZkR1Y0ZEQ0OGRHVjRkQ0I0UFNJeU1DSWdlVDBpTnpJaUlHWnZiblF0Wm1GdGFXeDVQU0luUTI5MWNtbGxjaUJPWlhjbkxFRnlhV0ZzTEcxdmJtOXpjR0ZqWlNJZ1ptOXVkQzF6YVhwbFBTSXlObkI0SWo1VGRISmxZVzFwYm1jOEwzUmxlSFErUEM5blBqeG5JR2xrUFNKQmJXOTFiblFpSUdacGJHdzlJaU5tWm1ZaVBqeHlaV04wSUhkcFpIUm9QU0l4TWpBaUlHaGxhV2RvZEQwaU1UQXdJaUJtYVd4c0xXOXdZV05wZEhrOUlpNHdNeUlnY25nOUlqRTFJaUJ5ZVQwaU1UVWlJSE4wY205clpUMGlJMlptWmlJZ2MzUnliMnRsTFc5d1lXTnBkSGs5SWk0eElpQnpkSEp2YTJVdGQybGtkR2c5SWpRaUx6NDhkR1Y0ZENCNFBTSXlNQ0lnZVQwaU16UWlJR1p2Ym5RdFptRnRhV3g1UFNJblEyOTFjbWxsY2lCT1pYY25MRUZ5YVdGc0xHMXZibTl6Y0dGalpTSWdabTl1ZEMxemFYcGxQU0l5TW5CNElqNUJiVzkxYm5ROEwzUmxlSFErUEhSbGVIUWdlRDBpTWpBaUlIazlJamN5SWlCbWIyNTBMV1poYldsc2VUMGlKME52ZFhKcFpYSWdUbVYzSnl4QmNtbGhiQ3h0YjI1dmMzQmhZMlVpSUdadmJuUXRjMmw2WlQwaU1qWndlQ0krSmlNNE9EQTFPeUF4TUVzOEwzUmxlSFErUEM5blBqeG5JR2xrUFNKRWRYSmhkR2x2YmlJZ1ptbHNiRDBpSTJabVppSStQSEpsWTNRZ2QybGtkR2c5SWpFMU1pSWdhR1ZwWjJoMFBTSXhNREFpSUdacGJHd3RiM0JoWTJsMGVUMGlMakF6SWlCeWVEMGlNVFVpSUhKNVBTSXhOU0lnYzNSeWIydGxQU0lqWm1abUlpQnpkSEp2YTJVdGIzQmhZMmwwZVQwaUxqRWlJSE4wY205clpTMTNhV1IwYUQwaU5DSXZQangwWlhoMElIZzlJakl3SWlCNVBTSXpOQ0lnWm05dWRDMW1ZVzFwYkhrOUlpZERiM1Z5YVdWeUlFNWxkeWNzUVhKcFlXd3NiVzl1YjNOd1lXTmxJaUJtYjI1MExYTnBlbVU5SWpJeWNIZ2lQa1IxY21GMGFXOXVQQzkwWlhoMFBqeDBaWGgwSUhnOUlqSXdJaUI1UFNJM01pSWdabTl1ZEMxbVlXMXBiSGs5SWlkRGIzVnlhV1Z5SUU1bGR5Y3NRWEpwWVd3c2JXOXViM053WVdObElpQm1iMjUwTFhOcGVtVTlJakkyY0hnaVBpWnNkRHNnTVNCRVlYazhMM1JsZUhRK1BDOW5Qand2WkdWbWN6NDhkR1Y0ZENCMFpYaDBMWEpsYm1SbGNtbHVaejBpYjNCMGFXMXBlbVZUY0dWbFpDSStQSFJsZUhSUVlYUm9JSE4wWVhKMFQyWm1jMlYwUFNJdE1UQXdKU0lnYUhKbFpqMGlJMFpzYjJGMGFXNW5WR1Y0ZENJZ1ptbHNiRDBpSTJabVppSWdabTl1ZEMxbVlXMXBiSGs5SWlkRGIzVnlhV1Z5SUU1bGR5Y3NRWEpwWVd3c2JXOXViM053WVdObElpQm1hV3hzTFc5d1lXTnBkSGs5SWk0NElpQm1iMjUwTFhOcGVtVTlJakkyY0hnaVBqeGhibWx0WVhSbElHRmtaR2wwYVhabFBTSnpkVzBpSUdGMGRISnBZblYwWlU1aGJXVTlJbk4wWVhKMFQyWm1jMlYwSWlCaVpXZHBiajBpTUhNaUlHUjFjajBpTlRCeklpQm1jbTl0UFNJd0pTSWdjbVZ3WldGMFEyOTFiblE5SW1sdVpHVm1hVzVwZEdVaUlIUnZQU0l4TURBbElpOCtNSGczTVdJNU9ETTNZV1V4WlRGaFl6azVOamMyWlRVeVlUSmlaVGd5WVRrNU9Ea3dNR1U0TW1NMklPS0FvaUJUWVdKc2FXVnlJRk5oWW14cFpYSWdURzlqYTNWd0lFeHBibVZoY2p3dmRHVjRkRkJoZEdnK1BIUmxlSFJRWVhSb0lITjBZWEowVDJabWMyVjBQU0l3SlNJZ2FISmxaajBpSTBac2IyRjBhVzVuVkdWNGRDSWdabWxzYkQwaUkyWm1aaUlnWm05dWRDMW1ZVzFwYkhrOUlpZERiM1Z5YVdWeUlFNWxkeWNzUVhKcFlXd3NiVzl1YjNOd1lXTmxJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDRJaUJtYjI1MExYTnBlbVU5SWpJMmNIZ2lQanhoYm1sdFlYUmxJR0ZrWkdsMGFYWmxQU0p6ZFcwaUlHRjBkSEpwWW5WMFpVNWhiV1U5SW5OMFlYSjBUMlptYzJWMElpQmlaV2RwYmowaU1ITWlJR1IxY2owaU5UQnpJaUJtY205dFBTSXdKU0lnY21Wd1pXRjBRMjkxYm5ROUltbHVaR1ZtYVc1cGRHVWlJSFJ2UFNJeE1EQWxJaTgrTUhnM01XSTVPRE0zWVdVeFpURmhZems1TmpjMlpUVXlZVEppWlRneVlUazVPRGt3TUdVNE1tTTJJT0tBb2lCVFlXSnNhV1Z5SUZOaFlteHBaWElnVEc5amEzVndJRXhwYm1WaGNqd3ZkR1Y0ZEZCaGRHZytQSFJsZUhSUVlYUm9JSE4wWVhKMFQyWm1jMlYwUFNJdE5UQWxJaUJvY21WbVBTSWpSbXh2WVhScGJtZFVaWGgwSWlCbWFXeHNQU0lqWm1abUlpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5eEJjbWxoYkN4dGIyNXZjM0JoWTJVaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpnaUlHWnZiblF0YzJsNlpUMGlNalp3ZUNJK1BHRnVhVzFoZEdVZ1lXUmthWFJwZG1VOUluTjFiU0lnWVhSMGNtbGlkWFJsVG1GdFpUMGljM1JoY25SUFptWnpaWFFpSUdKbFoybHVQU0l3Y3lJZ1pIVnlQU0kxTUhNaUlHWnliMjA5SWpBbElpQnlaWEJsWVhSRGIzVnVkRDBpYVc1a1pXWnBibWwwWlNJZ2RHODlJakV3TUNVaUx6NHdlRFUyTVRWa1pXSTNPVGhpWWpObE5HUm1ZVEF4TXpsa1ptRXhZak5rTkRNelkyTXlNMkkzTW1ZZzRvQ2lJRVJCU1R3dmRHVjRkRkJoZEdnK1BIUmxlSFJRWVhSb0lITjBZWEowVDJabWMyVjBQU0kxTUNVaUlHaHlaV1k5SWlOR2JHOWhkR2x1WjFSbGVIUWlJR1pwYkd3OUlpTm1abVlpSUdadmJuUXRabUZ0YVd4NVBTSW5RMjkxY21sbGNpQk9aWGNuTEVGeWFXRnNMRzF2Ym05emNHRmpaU0lnWm1sc2JDMXZjR0ZqYVhSNVBTSXVPQ0lnWm05dWRDMXphWHBsUFNJeU5uQjRJajQ4WVc1cGJXRjBaU0JoWkdScGRHbDJaVDBpYzNWdElpQmhkSFJ5YVdKMWRHVk9ZVzFsUFNKemRHRnlkRTltWm5ObGRDSWdZbVZuYVc0OUlqQnpJaUJrZFhJOUlqVXdjeUlnWm5KdmJUMGlNQ1VpSUhKbGNHVmhkRU52ZFc1MFBTSnBibVJsWm1sdWFYUmxJaUIwYnowaU1UQXdKU0l2UGpCNE5UWXhOV1JsWWpjNU9HSmlNMlUwWkdaaE1ERXpPV1JtWVRGaU0yUTBNek5qWXpJellqY3laaURpZ0tJZ1JFRkpQQzkwWlhoMFVHRjBhRDQ4TDNSbGVIUStQSFZ6WlNCb2NtVm1QU0lqUjJ4dmR5SWdabWxzYkMxdmNHRmphWFI1UFNJdU9TSXZQangxYzJVZ2FISmxaajBpSTBkc2IzY2lJSGc5SWpFd01EQWlJSGs5SWpFd01EQWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqa2lMejQ4ZFhObElHaHlaV1k5SWlOTWIyZHZJaUI0UFNJeE56QWlJSGs5SWpFM01DSWdkSEpoYm5ObWIzSnRQU0p6WTJGc1pTZ3VOaWtpTHo0OGRYTmxJR2h5WldZOUlpTkliM1Z5WjJ4aGMzTWlJSGc5SWpFMU1DSWdlVDBpT1RBaUlIUnlZVzV6Wm05eWJUMGljbTkwWVhSbEtERXdLU0lnZEhKaGJuTm1iM0p0TFc5eWFXZHBiajBpTlRBd0lEVXdNQ0l2UGp4MWMyVWdhSEpsWmowaUkxQnliMmR5WlhOeklpQjRQU0l4TkRRaUlIazlJamM1TUNJdlBqeDFjMlVnYUhKbFpqMGlJMU4wWVhSMWN5SWdlRDBpTXpZNElpQjVQU0kzT1RBaUx6NDhkWE5sSUdoeVpXWTlJaU5CYlc5MWJuUWlJSGc5SWpVMk9DSWdlVDBpTnprd0lpOCtQSFZ6WlNCb2NtVm1QU0lqUkhWeVlYUnBiMjRpSUhnOUlqY3dOQ0lnZVQwaU56a3dJaTgrUEM5emRtYysifQ"; - assertEq(actualTokenURI, expectedTokenURI, "token URI"); - } -} diff --git a/tests/integration/concrete/lockup-dynamic/token-uri/tokenURI.tree b/tests/integration/concrete/lockup-dynamic/token-uri/tokenURI.tree deleted file mode 100644 index 54723cea4..000000000 --- a/tests/integration/concrete/lockup-dynamic/token-uri/tokenURI.tree +++ /dev/null @@ -1,8 +0,0 @@ -TokenURI_Lockup_Dynamic_Integration_Concrete_Test -├── given NFT not exist -│ └── it should revert -└── given NFT exists - ├── when token URI decoded - │ └── it should return the correct token URI - └── when token URI not decoded - └── it should return the correct token URI diff --git a/tests/integration/concrete/lockup-linear/token-uri/tokenURI.t.sol b/tests/integration/concrete/lockup-linear/token-uri/tokenURI.t.sol deleted file mode 100644 index 834c1d610..000000000 --- a/tests/integration/concrete/lockup-linear/token-uri/tokenURI.t.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -// solhint-disable max-line-length,no-console,quotes -pragma solidity >=0.8.22 <0.9.0; - -import { IERC721Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; -import { console2 } from "forge-std/src/console2.sol"; -import { StdStyle } from "forge-std/src/StdStyle.sol"; -import { Base64 } from "solady/src/utils/Base64.sol"; - -import { Integration_Test } from "../LockupLinear.t.sol"; - -/// @dev Requirements for these tests to work: -/// - The stream ID must be 1 -/// - The stream's sender must be `0x6332e7b1deb1f1a0b77b2bb18b144330c7291bca`, i.e. `makeAddr("Sender")` -/// - The stream token must have the DAI symbol -/// - The contract deployer, i.e. the `sender` config option in `foundry.toml`, must have the default value -/// 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38 so that the deployed contracts have the same addresses as -/// the values hard coded in the tests below -contract TokenURI_Lockup_Linear_Integration_Concrete_Test is Integration_Test { - address internal constant LOCKUP = 0x3381cD18e2Fb4dB236BF0525938AB6E43Db0440f; - - /// @dev To make these tests noninvasive, they are run only when the contract address matches the hard coded value. - modifier skipOnMismatch() { - if (address(lockup) == LOCKUP) { - _; - } else { - console2.log(StdStyle.yellow('Warning: "LockupLinear.tokenURI" tests skipped due to address mismatch')); - } - } - - function test_RevertGiven_NFTNotExist() external { - vm.expectRevert(abi.encodeWithSelector(IERC721Errors.ERC721NonexistentToken.selector, nullStreamId)); - lockup.tokenURI({ tokenId: nullStreamId }); - } - - /// @dev If you need to update the hard-coded token URI: - /// 1. Use "vm.writeFile" to log the strings to a file. - /// 2. Remember to escape the EOL character \n with \\n. - function test_WhenTokenURIDecoded() external skipOnMismatch givenNFTExists { - vm.warp({ newTimestamp: defaults.START_TIME() + defaults.TOTAL_DURATION() / 4 }); - - string memory tokenURI = lockup.tokenURI(defaultStreamId); - 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":"DAI"},{"trait_type":"Sender","value":"0x6332e7b1deb1f1a0b77b2bb18b144330c7291bca"},{"trait_type":"Status","value":"Streaming"}],"description":"This NFT represents a payment stream in a Sablier Lockup Linear contract. The owner of this NFT can withdraw the streamed tokens, which are denominated in DAI.\\n\\n- Stream ID: 1\\n- Lockup Linear Address: 0x3381cd18e2fb4db236bf0525938ab6e43db0440f\\n- DAI Address: 0x03a6a84cd762d9707a21605b548aaab891562aab\\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 Linear #1","image":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAwIiBoZWlnaHQ9IjEwMDAiIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbHRlcj0idXJsKCNOb2lzZSkiLz48cmVjdCB4PSI3MCIgeT0iNzAiIHdpZHRoPSI4NjAiIGhlaWdodD0iODYwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4wMyIgcng9IjQ1IiByeT0iNDUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLW9wYWNpdHk9Ii4xIiBzdHJva2Utd2lkdGg9IjQiLz48ZGVmcz48Y2lyY2xlIGlkPSJHbG93IiByPSI1MDAiIGZpbGw9InVybCgjUmFkaWFsR2xvdykiLz48ZmlsdGVyIGlkPSJOb2lzZSI+PGZlRmxvb2QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmxvb2QtY29sb3I9ImhzbCgyMzAsMjElLDExJSkiIGZsb29kLW9wYWNpdHk9IjEiIHJlc3VsdD0iZmxvb2RGaWxsIi8+PGZlVHVyYnVsZW5jZSBiYXNlRnJlcXVlbmN5PSIuNCIgbnVtT2N0YXZlcz0iMyIgcmVzdWx0PSJOb2lzZSIgdHlwZT0iZnJhY3RhbE5vaXNlIi8+PGZlQmxlbmQgaW49Ik5vaXNlIiBpbjI9ImZsb29kRmlsbCIgbW9kZT0ic29mdC1saWdodCIvPjwvZmlsdGVyPjxwYXRoIGlkPSJMb2dvIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4xIiBkPSJtMTMzLjU1OSwxMjQuMDM0Yy0uMDEzLDIuNDEyLTEuMDU5LDQuODQ4LTIuOTIzLDYuNDAyLTIuNTU4LDEuODE5LTUuMTY4LDMuNDM5LTcuODg4LDQuOTk2LTE0LjQ0LDguMjYyLTMxLjA0NywxMi41NjUtNDcuNjc0LDEyLjU2OS04Ljg1OC4wMzYtMTcuODM4LTEuMjcyLTI2LjMyOC0zLjY2My05LjgwNi0yLjc2Ni0xOS4wODctNy4xMTMtMjcuNTYyLTEyLjc3OC0xMy44NDItOC4wMjUsOS40NjgtMjguNjA2LDE2LjE1My0zNS4yNjVoMGMyLjAzNS0xLjgzOCw0LjI1Mi0zLjU0Niw2LjQ2My01LjIyNGgwYzYuNDI5LTUuNjU1LDE2LjIxOC0yLjgzNSwyMC4zNTgsNC4xNyw0LjE0Myw1LjA1Nyw4LjgxNiw5LjY0OSwxMy45MiwxMy43MzRoLjAzN2M1LjczNiw2LjQ2MSwxNS4zNTctMi4yNTMsOS4zOC04LjQ4LDAsMC0zLjUxNS0zLjUxNS0zLjUxNS0zLjUxNS0xMS40OS0xMS40NzgtNTIuNjU2LTUyLjY2NC02NC44MzctNjQuODM3bC4wNDktLjAzN2MtMS43MjUtMS42MDYtMi43MTktMy44NDctMi43NTEtNi4yMDRoMGMtLjA0Ni0yLjM3NSwxLjA2Mi00LjU4MiwyLjcyNi02LjIyOWgwbC4xODUtLjE0OGgwYy4wOTktLjA2MiwuMjIyLS4xNDgsLjM3LS4yNTloMGMyLjA2LTEuMzYyLDMuOTUxLTIuNjIxLDYuMDQ0LTMuODQyQzU3Ljc2My0zLjQ3Myw5Ny43Ni0yLjM0MSwxMjguNjM3LDE4LjMzMmMxNi42NzEsOS45NDYtMjYuMzQ0LDU0LjgxMy0zOC42NTEsNDAuMTk5LTYuMjk5LTYuMDk2LTE4LjA2My0xNy43NDMtMTkuNjY4LTE4LjgxMS02LjAxNi00LjA0Ny0xMy4wNjEsNC43NzYtNy43NTIsOS43NTFsNjguMjU0LDY4LjM3MWMxLjcyNCwxLjYwMSwyLjcxNCwzLjg0LDIuNzM4LDYuMTkyWiIvPjxwYXRoIGlkPSJGbG9hdGluZ1RleHQiIGZpbGw9Im5vbmUiIGQ9Ik0xMjUgNDVoNzUwczgwIDAgODAgODB2NzUwczAgODAgLTgwIDgwaC03NTBzLTgwIDAgLTgwIC04MHYtNzUwczAgLTgwIDgwIC04MCIvPjxyYWRpYWxHcmFkaWVudCBpZD0iUmFkaWFsR2xvdyI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iaHNsKDE5LDIyJSw2MyUpIiBzdG9wLW9wYWNpdHk9Ii42Ii8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSJoc2woMjMwLDIxJSwxMSUpIiBzdG9wLW9wYWNpdHk9IjAiLz48L3JhZGlhbEdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iU2FuZFRvcCIgeDE9IjAlIiB5MT0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9ImhzbCgxOSwyMiUsNjMlKSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iaHNsKDIzMCwyMSUsMTElKSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJTYW5kQm90dG9tIiB4MT0iMTAwJSIgeTE9IjEwMCUiPjxzdG9wIG9mZnNldD0iMTAlIiBzdG9wLWNvbG9yPSJoc2woMjMwLDIxJSwxMSUpIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSJoc2woMTksMjIlLDYzJSkiLz48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ4MSIgZHVyPSI2cyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHZhbHVlcz0iMzAlOzYwJTsxMjAlOzYwJTszMCU7Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9IkhvdXJnbGFzc1N0cm9rZSIgZ3JhZGllbnRUcmFuc2Zvcm09InJvdGF0ZSg5MCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iaHNsKDE5LDIyJSw2MyUpIi8+PHN0b3Agb2Zmc2V0PSI4MCUiIHN0b3AtY29sb3I9ImhzbCgyMzAsMjElLDExJSkiLz48L2xpbmVhckdyYWRpZW50PjxnIGlkPSJIb3VyZ2xhc3MiPjxwYXRoIGQ9Ik0gNTAsMzYwIGEgMzAwLDMwMCAwIDEsMSA2MDAsMCBhIDMwMCwzMDAgMCAxLDEgLTYwMCwwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4wMiIgc3Ryb2tlPSJ1cmwoI0hvdXJnbGFzc1N0cm9rZSkiIHN0cm9rZS13aWR0aD0iNCIvPjxwYXRoIGQ9Im01NjYsMTYxLjIwMXYtNTMuOTI0YzAtMTkuMzgyLTIyLjUxMy0zNy41NjMtNjMuMzk4LTUxLjE5OC00MC43NTYtMTMuNTkyLTk0Ljk0Ni0yMS4wNzktMTUyLjU4Ny0yMS4wNzlzLTExMS44MzgsNy40ODctMTUyLjYwMiwyMS4wNzljLTQwLjg5MywxMy42MzYtNjMuNDEzLDMxLjgxNi02My40MTMsNTEuMTk4djUzLjkyNGMwLDE3LjE4MSwxNy43MDQsMzMuNDI3LDUwLjIyMyw0Ni4zOTR2Mjg0LjgwOWMtMzIuNTE5LDEyLjk2LTUwLjIyMywyOS4yMDYtNTAuMjIzLDQ2LjM5NHY1My45MjRjMCwxOS4zODIsMjIuNTIsMzcuNTYzLDYzLjQxMyw1MS4xOTgsNDAuNzYzLDEzLjU5Miw5NC45NTQsMjEuMDc5LDE1Mi42MDIsMjEuMDc5czExMS44MzEtNy40ODcsMTUyLjU4Ny0yMS4wNzljNDAuODg2LTEzLjYzNiw2My4zOTgtMzEuODE2LDYzLjM5OC01MS4xOTh2LTUzLjkyNGMwLTE3LjE5Ni0xNy43MDQtMzMuNDM1LTUwLjIyMy00Ni40MDFWMjA3LjYwM2MzMi41MTktMTIuOTY3LDUwLjIyMy0yOS4yMDYsNTAuMjIzLTQ2LjQwMVptLTM0Ny40NjIsNTcuNzkzbDEzMC45NTksMTMxLjAyNy0xMzAuOTU5LDEzMS4wMTNWMjE4Ljk5NFptMjYyLjkyNC4wMjJ2MjYyLjAxOGwtMTMwLjkzNy0xMzEuMDA2LDEzMC45MzctMTMxLjAxM1oiIGZpbGw9IiMxNjE4MjIiPjwvcGF0aD48cG9seWdvbiBwb2ludHM9IjM1MCAzNTAuMDI2IDQxNS4wMyAyODQuOTc4IDI4NSAyODQuOTc4IDM1MCAzNTAuMDI2IiBmaWxsPSJ1cmwoI1NhbmRCb3R0b20pIi8+PHBhdGggZD0ibTQxNi4zNDEsMjgxLjk3NWMwLC45MTQtLjM1NCwxLjgwOS0xLjAzNSwyLjY4LTUuNTQyLDcuMDc2LTMyLjY2MSwxMi40NS02NS4yOCwxMi40NS0zMi42MjQsMC01OS43MzgtNS4zNzQtNjUuMjgtMTIuNDUtLjY4MS0uODcyLTEuMDM1LTEuNzY3LTEuMDM1LTIuNjgsMC0uOTE0LjM1NC0xLjgwOCwxLjAzNS0yLjY3Niw1LjU0Mi03LjA3NiwzMi42NTYtMTIuNDUsNjUuMjgtMTIuNDUsMzIuNjE5LDAsNTkuNzM4LDUuMzc0LDY1LjI4LDEyLjQ1LjY4MS44NjcsMS4wMzUsMS43NjIsMS4wMzUsMi42NzZaIiBmaWxsPSJ1cmwoI1NhbmRUb3ApIi8+PHBhdGggZD0ibTQ4MS40Niw1MDQuMTAxdjU4LjQ0OWMtMi4zNS43Ny00LjgyLDEuNTEtNy4zOSwyLjIzLTMwLjMsOC41NC03NC42NSwxMy45Mi0xMjQuMDYsMTMuOTItNTMuNiwwLTEwMS4yNC02LjMzLTEzMS40Ny0xNi4xNnYtNTguNDM5aDI2Mi45MloiIGZpbGw9InVybCgjU2FuZEJvdHRvbSkiLz48ZWxsaXBzZSBjeD0iMzUwIiBjeT0iNTA0LjEwMSIgcng9IjEzMS40NjIiIHJ5PSIyOC4xMDgiIGZpbGw9InVybCgjU2FuZFRvcCkiLz48ZyBmaWxsPSJub25lIiBzdHJva2U9InVybCgjSG91cmdsYXNzU3Ryb2tlKSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iNCI+PHBhdGggZD0ibTU2NS42NDEsMTA3LjI4YzAsOS41MzctNS41NiwxOC42MjktMTUuNjc2LDI2Ljk3M2gtLjAyM2MtOS4yMDQsNy41OTYtMjIuMTk0LDE0LjU2Mi0zOC4xOTcsMjAuNTkyLTM5LjUwNCwxNC45MzYtOTcuMzI1LDI0LjM1NS0xNjEuNzMzLDI0LjM1NS05MC40OCwwLTE2Ny45NDgtMTguNTgyLTE5OS45NTMtNDQuOTQ4aC0uMDIzYy0xMC4xMTUtOC4zNDQtMTUuNjc2LTE3LjQzNy0xNS42NzYtMjYuOTczLDAtMzkuNzM1LDk2LjU1NC03MS45MjEsMjE1LjY1Mi03MS45MjFzMjE1LjYyOSwzMi4xODUsMjE1LjYyOSw3MS45MjFaIi8+PHBhdGggZD0ibTEzNC4zNiwxNjEuMjAzYzAsMzkuNzM1LDk2LjU1NCw3MS45MjEsMjE1LjY1Miw3MS45MjFzMjE1LjYyOS0zMi4xODYsMjE1LjYyOS03MS45MjEiLz48bGluZSB4MT0iMTM0LjM2IiB5MT0iMTYxLjIwMyIgeDI9IjEzNC4zNiIgeTI9IjEwNy4yOCIvPjxsaW5lIHgxPSI1NjUuNjQiIHkxPSIxNjEuMjAzIiB4Mj0iNTY1LjY0IiB5Mj0iMTA3LjI4Ii8+PGxpbmUgeDE9IjE4NC41ODQiIHkxPSIyMDYuODIzIiB4Mj0iMTg0LjU4NSIgeTI9IjUzNy41NzkiLz48bGluZSB4MT0iMjE4LjE4MSIgeTE9IjIxOC4xMTgiIHgyPSIyMTguMTgxIiB5Mj0iNTYyLjUzNyIvPjxsaW5lIHgxPSI0ODEuODE4IiB5MT0iMjE4LjE0MiIgeDI9IjQ4MS44MTkiIHkyPSI1NjIuNDI4Ii8+PGxpbmUgeDE9IjUxNS40MTUiIHkxPSIyMDcuMzUyIiB4Mj0iNTE1LjQxNiIgeTI9IjUzNy41NzkiLz48cGF0aCBkPSJtMTg0LjU4LDUzNy41OGMwLDUuNDUsNC4yNywxMC42NSwxMi4wMywxNS40MmguMDJjNS41MSwzLjM5LDEyLjc5LDYuNTUsMjEuNTUsOS40MiwzMC4yMSw5LjksNzguMDIsMTYuMjgsMTMxLjgzLDE2LjI4LDQ5LjQxLDAsOTMuNzYtNS4zOCwxMjQuMDYtMTMuOTIsMi43LS43Niw1LjI5LTEuNTQsNy43NS0yLjM1LDguNzctMi44NywxNi4wNS02LjA0LDIxLjU2LTkuNDNoMGM3Ljc2LTQuNzcsMTIuMDQtOS45NywxMi4wNC0xNS40MiIvPjxwYXRoIGQ9Im0xODQuNTgyLDQ5Mi42NTZjLTMxLjM1NCwxMi40ODUtNTAuMjIzLDI4LjU4LTUwLjIyMyw0Ni4xNDIsMCw5LjUzNiw1LjU2NCwxOC42MjcsMTUuNjc3LDI2Ljk2OWguMDIyYzguNTAzLDcuMDA1LDIwLjIxMywxMy40NjMsMzQuNTI0LDE5LjE1OSw5Ljk5OSwzLjk5MSwyMS4yNjksNy42MDksMzMuNTk3LDEwLjc4OCwzNi40NSw5LjQwNyw4Mi4xODEsMTUuMDAyLDEzMS44MzUsMTUuMDAyczk1LjM2My01LjU5NSwxMzEuODA3LTE1LjAwMmMxMC44NDctMi43OSwyMC44NjctNS45MjYsMjkuOTI0LTkuMzQ5LDEuMjQ0LS40NjcsMi40NzMtLjk0MiwzLjY3My0xLjQyNCwxNC4zMjYtNS42OTYsMjYuMDM1LTEyLjE2MSwzNC41MjQtMTkuMTczaC4wMjJjMTAuMTE0LTguMzQyLDE1LjY3Ny0xNy40MzMsMTUuNjc3LTI2Ljk2OSwwLTE3LjU2Mi0xOC44NjktMzMuNjY1LTUwLjIyMy00Ni4xNSIvPjxwYXRoIGQ9Im0xMzQuMzYsNTkyLjcyYzAsMzkuNzM1LDk2LjU1NCw3MS45MjEsMjE1LjY1Miw3MS45MjFzMjE1LjYyOS0zMi4xODYsMjE1LjYyOS03MS45MjEiLz48bGluZSB4MT0iMTM0LjM2IiB5MT0iNTkyLjcyIiB4Mj0iMTM0LjM2IiB5Mj0iNTM4Ljc5NyIvPjxsaW5lIHgxPSI1NjUuNjQiIHkxPSI1OTIuNzIiIHgyPSI1NjUuNjQiIHkyPSI1MzguNzk3Ii8+PHBvbHlsaW5lIHBvaW50cz0iNDgxLjgyMiA0ODEuOTAxIDQ4MS43OTggNDgxLjg3NyA0ODEuNzc1IDQ4MS44NTQgMzUwLjAxNSAzNTAuMDI2IDIxOC4xODUgMjE4LjEyOSIvPjxwb2x5bGluZSBwb2ludHM9IjIxOC4xODUgNDgxLjkwMSAyMTguMjMxIDQ4MS44NTQgMzUwLjAxNSAzNTAuMDI2IDQ4MS44MjIgMjE4LjE1MiIvPjwvZz48L2c+PGcgaWQ9IlByb2dyZXNzIiBmaWxsPSIjZmZmIj48cmVjdCB3aWR0aD0iMjA4IiBoZWlnaHQ9IjEwMCIgZmlsbC1vcGFjaXR5PSIuMDMiIHJ4PSIxNSIgcnk9IjE1IiBzdHJva2U9IiNmZmYiIHN0cm9rZS1vcGFjaXR5PSIuMSIgc3Ryb2tlLXdpZHRoPSI0Ii8+PHRleHQgeD0iMjAiIHk9IjM0IiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjJweCI+UHJvZ3Jlc3M8L3RleHQ+PHRleHQgeD0iMjAiIHk9IjcyIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjZweCI+MjUlPC90ZXh0PjxnIGZpbGw9Im5vbmUiPjxjaXJjbGUgY3g9IjE2NiIgY3k9IjUwIiByPSIyMiIgc3Ryb2tlPSJoc2woMjMwLDIxJSwxMSUpIiBzdHJva2Utd2lkdGg9IjEwIi8+PGNpcmNsZSBjeD0iMTY2IiBjeT0iNTAiIHBhdGhMZW5ndGg9IjEwMDAwIiByPSIyMiIgc3Ryb2tlPSJoc2woMTksMjIlLDYzJSkiIHN0cm9rZS1kYXNoYXJyYXk9IjEwMDAwIiBzdHJva2UtZGFzaG9mZnNldD0iNzUwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjUiIHRyYW5zZm9ybT0icm90YXRlKC05MCkiIHRyYW5zZm9ybS1vcmlnaW49IjE2NiA1MCIvPjwvZz48L2c+PGcgaWQ9IlN0YXR1cyIgZmlsbD0iI2ZmZiI+PHJlY3Qgd2lkdGg9IjE4NCIgaGVpZ2h0PSIxMDAiIGZpbGwtb3BhY2l0eT0iLjAzIiByeD0iMTUiIHJ5PSIxNSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZS13aWR0aD0iNCIvPjx0ZXh0IHg9IjIwIiB5PSIzNCIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjIycHgiPlN0YXR1czwvdGV4dD48dGV4dCB4PSIyMCIgeT0iNzIiIGZvbnQtZmFtaWx5PSInQ291cmllciBOZXcnLEFyaWFsLG1vbm9zcGFjZSIgZm9udC1zaXplPSIyNnB4Ij5TdHJlYW1pbmc8L3RleHQ+PC9nPjxnIGlkPSJBbW91bnQiIGZpbGw9IiNmZmYiPjxyZWN0IHdpZHRoPSIxMjAiIGhlaWdodD0iMTAwIiBmaWxsLW9wYWNpdHk9Ii4wMyIgcng9IjE1IiByeT0iMTUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLW9wYWNpdHk9Ii4xIiBzdHJva2Utd2lkdGg9IjQiLz48dGV4dCB4PSIyMCIgeT0iMzQiIGZvbnQtZmFtaWx5PSInQ291cmllciBOZXcnLEFyaWFsLG1vbm9zcGFjZSIgZm9udC1zaXplPSIyMnB4Ij5BbW91bnQ8L3RleHQ+PHRleHQgeD0iMjAiIHk9IjcyIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjZweCI+JiM4ODA1OyAxMEs8L3RleHQ+PC9nPjxnIGlkPSJEdXJhdGlvbiIgZmlsbD0iI2ZmZiI+PHJlY3Qgd2lkdGg9IjE1MiIgaGVpZ2h0PSIxMDAiIGZpbGwtb3BhY2l0eT0iLjAzIiByeD0iMTUiIHJ5PSIxNSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZS13aWR0aD0iNCIvPjx0ZXh0IHg9IjIwIiB5PSIzNCIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjIycHgiPkR1cmF0aW9uPC90ZXh0Pjx0ZXh0IHg9IjIwIiB5PSI3MiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjI2cHgiPiZsdDsgMSBEYXk8L3RleHQ+PC9nPjwvZGVmcz48dGV4dCB0ZXh0LXJlbmRlcmluZz0ib3B0aW1pemVTcGVlZCI+PHRleHRQYXRoIHN0YXJ0T2Zmc2V0PSItMTAwJSIgaHJlZj0iI0Zsb2F0aW5nVGV4dCIgZmlsbD0iI2ZmZiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmaWxsLW9wYWNpdHk9Ii44IiBmb250LXNpemU9IjI2cHgiPjxhbmltYXRlIGFkZGl0aXZlPSJzdW0iIGF0dHJpYnV0ZU5hbWU9InN0YXJ0T2Zmc2V0IiBiZWdpbj0iMHMiIGR1cj0iNTBzIiBmcm9tPSIwJSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHRvPSIxMDAlIi8+MHgzMzgxY2QxOGUyZmI0ZGIyMzZiZjA1MjU5MzhhYjZlNDNkYjA0NDBmIOKAoiBTYWJsaWVyIFYyIExvY2t1cCBMaW5lYXI8L3RleHRQYXRoPjx0ZXh0UGF0aCBzdGFydE9mZnNldD0iMCUiIGhyZWY9IiNGbG9hdGluZ1RleHQiIGZpbGw9IiNmZmYiIGZvbnQtZmFtaWx5PSInQ291cmllciBOZXcnLEFyaWFsLG1vbm9zcGFjZSIgZmlsbC1vcGFjaXR5PSIuOCIgZm9udC1zaXplPSIyNnB4Ij48YW5pbWF0ZSBhZGRpdGl2ZT0ic3VtIiBhdHRyaWJ1dGVOYW1lPSJzdGFydE9mZnNldCIgYmVnaW49IjBzIiBkdXI9IjUwcyIgZnJvbT0iMCUiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB0bz0iMTAwJSIvPjB4MzM4MWNkMThlMmZiNGRiMjM2YmYwNTI1OTM4YWI2ZTQzZGIwNDQwZiDigKIgU2FibGllciBWMiBMb2NrdXAgTGluZWFyPC90ZXh0UGF0aD48dGV4dFBhdGggc3RhcnRPZmZzZXQ9Ii01MCUiIGhyZWY9IiNGbG9hdGluZ1RleHQiIGZpbGw9IiNmZmYiIGZvbnQtZmFtaWx5PSInQ291cmllciBOZXcnLEFyaWFsLG1vbm9zcGFjZSIgZmlsbC1vcGFjaXR5PSIuOCIgZm9udC1zaXplPSIyNnB4Ij48YW5pbWF0ZSBhZGRpdGl2ZT0ic3VtIiBhdHRyaWJ1dGVOYW1lPSJzdGFydE9mZnNldCIgYmVnaW49IjBzIiBkdXI9IjUwcyIgZnJvbT0iMCUiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB0bz0iMTAwJSIvPjB4MDNhNmE4NGNkNzYyZDk3MDdhMjE2MDViNTQ4YWFhYjg5MTU2MmFhYiDigKIgREFJPC90ZXh0UGF0aD48dGV4dFBhdGggc3RhcnRPZmZzZXQ9IjUwJSIgaHJlZj0iI0Zsb2F0aW5nVGV4dCIgZmlsbD0iI2ZmZiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmaWxsLW9wYWNpdHk9Ii44IiBmb250LXNpemU9IjI2cHgiPjxhbmltYXRlIGFkZGl0aXZlPSJzdW0iIGF0dHJpYnV0ZU5hbWU9InN0YXJ0T2Zmc2V0IiBiZWdpbj0iMHMiIGR1cj0iNTBzIiBmcm9tPSIwJSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHRvPSIxMDAlIi8+MHgwM2E2YTg0Y2Q3NjJkOTcwN2EyMTYwNWI1NDhhYWFiODkxNTYyYWFiIOKAoiBEQUk8L3RleHRQYXRoPjwvdGV4dD48dXNlIGhyZWY9IiNHbG93IiBmaWxsLW9wYWNpdHk9Ii45Ii8+PHVzZSBocmVmPSIjR2xvdyIgeD0iMTAwMCIgeT0iMTAwMCIgZmlsbC1vcGFjaXR5PSIuOSIvPjx1c2UgaHJlZj0iI0xvZ28iIHg9IjE3MCIgeT0iMTcwIiB0cmFuc2Zvcm09InNjYWxlKC42KSIvPjx1c2UgaHJlZj0iI0hvdXJnbGFzcyIgeD0iMTUwIiB5PSI5MCIgdHJhbnNmb3JtPSJyb3RhdGUoMTApIiB0cmFuc2Zvcm0tb3JpZ2luPSI1MDAgNTAwIi8+PHVzZSBocmVmPSIjUHJvZ3Jlc3MiIHg9IjE0NCIgeT0iNzkwIi8+PHVzZSBocmVmPSIjU3RhdHVzIiB4PSIzNjgiIHk9Ijc5MCIvPjx1c2UgaHJlZj0iI0Ftb3VudCIgeD0iNTY4IiB5PSI3OTAiLz48dXNlIGhyZWY9IiNEdXJhdGlvbiIgeD0iNzA0IiB5PSI3OTAiLz48L3N2Zz4="}'; - assertEq(actualDecodedTokenURI, expectedDecodedTokenURI, "decoded token URI"); - } - - function test_WhenTokenURINotDecoded() external skipOnMismatch givenNFTExists { - vm.warp({ newTimestamp: defaults.START_TIME() + defaults.TOTAL_DURATION() / 4 }); - - string memory actualTokenURI = lockup.tokenURI(defaultStreamId); - string memory expectedTokenURI = - "data:application/json;base64,eyJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoiQXNzZXQiLCJ2YWx1ZSI6IkRBSSJ9LHsidHJhaXRfdHlwZSI6IlNlbmRlciIsInZhbHVlIjoiMHg2MzMyZTdiMWRlYjFmMWEwYjc3YjJiYjE4YjE0NDMzMGM3MjkxYmNhIn0seyJ0cmFpdF90eXBlIjoiU3RhdHVzIiwidmFsdWUiOiJTdHJlYW1pbmcifV0sImRlc2NyaXB0aW9uIjoiVGhpcyBORlQgcmVwcmVzZW50cyBhIHBheW1lbnQgc3RyZWFtIGluIGEgU2FibGllciBWMiBMb2NrdXAgTGluZWFyIGNvbnRyYWN0LiBUaGUgb3duZXIgb2YgdGhpcyBORlQgY2FuIHdpdGhkcmF3IHRoZSBzdHJlYW1lZCBhc3NldHMsIHdoaWNoIGFyZSBkZW5vbWluYXRlZCBpbiBEQUkuXG5cbi0gU3RyZWFtIElEOiAxXG4tIExvY2t1cCBMaW5lYXIgQWRkcmVzczogMHgzMzgxY2QxOGUyZmI0ZGIyMzZiZjA1MjU5MzhhYjZlNDNkYjA0NDBmXG4tIERBSSBBZGRyZXNzOiAweDAzYTZhODRjZDc2MmQ5NzA3YTIxNjA1YjU0OGFhYWI4OTE1NjJhYWJcblxu4pqg77iPIFdBUk5JTkc6IFRyYW5zZmVycmluZyB0aGUgTkZUIG1ha2VzIHRoZSBuZXcgb3duZXIgdGhlIHJlY2lwaWVudCBvZiB0aGUgc3RyZWFtLiBUaGUgZnVuZHMgYXJlIG5vdCBhdXRvbWF0aWNhbGx5IHdpdGhkcmF3biBmb3IgdGhlIHByZXZpb3VzIHJlY2lwaWVudC4iLCJleHRlcm5hbF91cmwiOiJodHRwczovL3NhYmxpZXIuY29tIiwibmFtZSI6IlNhYmxpZXIgVjIgTG9ja3VwIExpbmVhciAjMSIsImltYWdlIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0l4TURBd0lpQm9aV2xuYUhROUlqRXdNREFpSUhacFpYZENiM2c5SWpBZ01DQXhNREF3SURFd01EQWlQanh5WldOMElIZHBaSFJvUFNJeE1EQWxJaUJvWldsbmFIUTlJakV3TUNVaUlHWnBiSFJsY2owaWRYSnNLQ05PYjJselpTa2lMejQ4Y21WamRDQjRQU0kzTUNJZ2VUMGlOekFpSUhkcFpIUm9QU0k0TmpBaUlHaGxhV2RvZEQwaU9EWXdJaUJtYVd4c1BTSWpabVptSWlCbWFXeHNMVzl3WVdOcGRIazlJaTR3TXlJZ2NuZzlJalExSWlCeWVUMGlORFVpSUhOMGNtOXJaVDBpSTJabVppSWdjM1J5YjJ0bExXOXdZV05wZEhrOUlpNHhJaUJ6ZEhKdmEyVXRkMmxrZEdnOUlqUWlMejQ4WkdWbWN6NDhZMmx5WTJ4bElHbGtQU0pIYkc5M0lpQnlQU0kxTURBaUlHWnBiR3c5SW5WeWJDZ2pVbUZrYVdGc1IyeHZkeWtpTHo0OFptbHNkR1Z5SUdsa1BTSk9iMmx6WlNJK1BHWmxSbXh2YjJRZ2VEMGlNQ0lnZVQwaU1DSWdkMmxrZEdnOUlqRXdNQ1VpSUdobGFXZG9kRDBpTVRBd0pTSWdabXh2YjJRdFkyOXNiM0k5SW1oemJDZ3lNekFzTWpFbExERXhKU2tpSUdac2IyOWtMVzl3WVdOcGRIazlJakVpSUhKbGMzVnNkRDBpWm14dmIyUkdhV3hzSWk4K1BHWmxWSFZ5WW5Wc1pXNWpaU0JpWVhObFJuSmxjWFZsYm1ONVBTSXVOQ0lnYm5WdFQyTjBZWFpsY3owaU15SWdjbVZ6ZFd4MFBTSk9iMmx6WlNJZ2RIbHdaVDBpWm5KaFkzUmhiRTV2YVhObElpOCtQR1psUW14bGJtUWdhVzQ5SWs1dmFYTmxJaUJwYmpJOUltWnNiMjlrUm1sc2JDSWdiVzlrWlQwaWMyOW1kQzFzYVdkb2RDSXZQand2Wm1sc2RHVnlQanh3WVhSb0lHbGtQU0pNYjJkdklpQm1hV3hzUFNJalptWm1JaUJtYVd4c0xXOXdZV05wZEhrOUlpNHhJaUJrUFNKdE1UTXpMalUxT1N3eE1qUXVNRE0wWXkwdU1ERXpMREl1TkRFeUxURXVNRFU1TERRdU9EUTRMVEl1T1RJekxEWXVOREF5TFRJdU5UVTRMREV1T0RFNUxUVXVNVFk0TERNdU5ETTVMVGN1T0RnNExEUXVPVGsyTFRFMExqUTBMRGd1TWpZeUxUTXhMakEwTnl3eE1pNDFOalV0TkRjdU5qYzBMREV5TGpVMk9TMDRMamcxT0M0d016WXRNVGN1T0RNNExURXVNamN5TFRJMkxqTXlPQzB6TGpZMk15MDVMamd3TmkweUxqYzJOaTB4T1M0d09EY3ROeTR4TVRNdE1qY3VOVFl5TFRFeUxqYzNPQzB4TXk0NE5ESXRPQzR3TWpVc09TNDBOamd0TWpndU5qQTJMREUyTGpFMU15MHpOUzR5TmpWb01HTXlMakF6TlMweExqZ3pPQ3cwTGpJMU1pMHpMalUwTml3MkxqUTJNeTAxTGpJeU5HZ3dZell1TkRJNUxUVXVOalUxTERFMkxqSXhPQzB5TGpnek5Td3lNQzR6TlRnc05DNHhOeXcwTGpFME15dzFMakExTnl3NExqZ3hOaXc1TGpZME9Td3hNeTQ1TWl3eE15NDNNelJvTGpBek4yTTFMamN6Tml3MkxqUTJNU3d4TlM0ek5UY3RNaTR5TlRNc09TNHpPQzA0TGpRNExEQXNNQzB6TGpVeE5TMHpMalV4TlMwekxqVXhOUzB6TGpVeE5TMHhNUzQwT1MweE1TNDBOemd0TlRJdU5qVTJMVFV5TGpZMk5DMDJOQzQ0TXpjdE5qUXVPRE0zYkM0d05Ea3RMakF6TjJNdE1TNDNNalV0TVM0Mk1EWXRNaTQzTVRrdE15NDRORGN0TWk0M05URXROaTR5TURSb01HTXRMakEwTmkweUxqTTNOU3d4TGpBMk1pMDBMalU0TWl3eUxqY3lOaTAyTGpJeU9XZ3diQzR4T0RVdExqRTBPR2d3WXk0d09Ua3RMakEyTWl3dU1qSXlMUzR4TkRnc0xqTTNMUzR5TlRsb01HTXlMakEyTFRFdU16WXlMRE11T1RVeExUSXVOakl4TERZdU1EUTBMVE11T0RReVF6VTNMamMyTXkwekxqUTNNeXc1Tnk0M05pMHlMak0wTVN3eE1qZ3VOak0zTERFNExqTXpNbU14Tmk0Mk56RXNPUzQ1TkRZdE1qWXVNelEwTERVMExqZ3hNeTB6T0M0Mk5URXNOREF1TVRrNUxUWXVNams1TFRZdU1EazJMVEU0TGpBMk15MHhOeTQzTkRNdE1Ua3VOalk0TFRFNExqZ3hNUzAyTGpBeE5pMDBMakEwTnkweE15NHdOakVzTkM0M056WXROeTQzTlRJc09TNDNOVEZzTmpndU1qVTBMRFk0TGpNM01XTXhMamN5TkN3eExqWXdNU3d5TGpjeE5Dd3pMamcwTERJdU56TTRMRFl1TVRreVdpSXZQanh3WVhSb0lHbGtQU0pHYkc5aGRHbHVaMVJsZUhRaUlHWnBiR3c5SW01dmJtVWlJR1E5SWsweE1qVWdORFZvTnpVd2N6Z3dJREFnT0RBZ09EQjJOelV3Y3pBZ09EQWdMVGd3SURnd2FDMDNOVEJ6TFRnd0lEQWdMVGd3SUMwNE1IWXROelV3Y3pBZ0xUZ3dJRGd3SUMwNE1DSXZQanh5WVdScFlXeEhjbUZrYVdWdWRDQnBaRDBpVW1Ga2FXRnNSMnh2ZHlJK1BITjBiM0FnYjJabWMyVjBQU0l3SlNJZ2MzUnZjQzFqYjJ4dmNqMGlhSE5zS0RFNUxESXlKU3cyTXlVcElpQnpkRzl3TFc5d1lXTnBkSGs5SWk0MklpOCtQSE4wYjNBZ2IyWm1jMlYwUFNJeE1EQWxJaUJ6ZEc5d0xXTnZiRzl5UFNKb2Myd29Nak13TERJeEpTd3hNU1VwSWlCemRHOXdMVzl3WVdOcGRIazlJakFpTHo0OEwzSmhaR2xoYkVkeVlXUnBaVzUwUGp4c2FXNWxZWEpIY21Ga2FXVnVkQ0JwWkQwaVUyRnVaRlJ2Y0NJZ2VERTlJakFsSWlCNU1UMGlNQ1VpUGp4emRHOXdJRzltWm5ObGREMGlNQ1VpSUhOMGIzQXRZMjlzYjNJOUltaHpiQ2d4T1N3eU1pVXNOak1sS1NJdlBqeHpkRzl3SUc5bVpuTmxkRDBpTVRBd0pTSWdjM1J2Y0MxamIyeHZjajBpYUhOc0tESXpNQ3d5TVNVc01URWxLU0l2UGp3dmJHbHVaV0Z5UjNKaFpHbGxiblErUEd4cGJtVmhja2R5WVdScFpXNTBJR2xrUFNKVFlXNWtRbTkwZEc5dElpQjRNVDBpTVRBd0pTSWdlVEU5SWpFd01DVWlQanh6ZEc5d0lHOW1abk5sZEQwaU1UQWxJaUJ6ZEc5d0xXTnZiRzl5UFNKb2Myd29Nak13TERJeEpTd3hNU1VwSWk4K1BITjBiM0FnYjJabWMyVjBQU0l4TURBbElpQnpkRzl3TFdOdmJHOXlQU0pvYzJ3b01Ua3NNaklsTERZekpTa2lMejQ4WVc1cGJXRjBaU0JoZEhSeWFXSjFkR1ZPWVcxbFBTSjRNU0lnWkhWeVBTSTJjeUlnY21Wd1pXRjBRMjkxYm5ROUltbHVaR1ZtYVc1cGRHVWlJSFpoYkhWbGN6MGlNekFsT3pZd0pUc3hNakFsT3pZd0pUc3pNQ1U3SWk4K1BDOXNhVzVsWVhKSGNtRmthV1Z1ZEQ0OGJHbHVaV0Z5UjNKaFpHbGxiblFnYVdROUlraHZkWEpuYkdGemMxTjBjbTlyWlNJZ1ozSmhaR2xsYm5SVWNtRnVjMlp2Y20wOUluSnZkR0YwWlNnNU1Da2lJR2R5WVdScFpXNTBWVzVwZEhNOUluVnpaWEpUY0dGalpVOXVWWE5sSWo0OGMzUnZjQ0J2Wm1aelpYUTlJalV3SlNJZ2MzUnZjQzFqYjJ4dmNqMGlhSE5zS0RFNUxESXlKU3cyTXlVcElpOCtQSE4wYjNBZ2IyWm1jMlYwUFNJNE1DVWlJSE4wYjNBdFkyOXNiM0k5SW1oemJDZ3lNekFzTWpFbExERXhKU2tpTHo0OEwyeHBibVZoY2tkeVlXUnBaVzUwUGp4bklHbGtQU0pJYjNWeVoyeGhjM01pUGp4d1lYUm9JR1E5SWswZ05UQXNNell3SUdFZ016QXdMRE13TUNBd0lERXNNU0EyTURBc01DQmhJRE13TUN3ek1EQWdNQ0F4TERFZ0xUWXdNQ3d3SWlCbWFXeHNQU0lqWm1abUlpQm1hV3hzTFc5d1lXTnBkSGs5SWk0d01pSWdjM1J5YjJ0bFBTSjFjbXdvSTBodmRYSm5iR0Z6YzFOMGNtOXJaU2tpSUhOMGNtOXJaUzEzYVdSMGFEMGlOQ0l2UGp4d1lYUm9JR1E5SW0wMU5qWXNNVFl4TGpJd01YWXROVE11T1RJMFl6QXRNVGt1TXpneUxUSXlMalV4TXkwek55NDFOak10TmpNdU16azRMVFV4TGpFNU9DMDBNQzQzTlRZdE1UTXVOVGt5TFRrMExqazBOaTB5TVM0d056a3RNVFV5TGpVNE55MHlNUzR3TnpsekxURXhNUzQ0TXpnc055NDBPRGN0TVRVeUxqWXdNaXd5TVM0d056bGpMVFF3TGpnNU15d3hNeTQyTXpZdE5qTXVOREV6TERNeExqZ3hOaTAyTXk0ME1UTXNOVEV1TVRrNGRqVXpMamt5TkdNd0xERTNMakU0TVN3eE55NDNNRFFzTXpNdU5ESTNMRFV3TGpJeU15dzBOaTR6T1RSMk1qZzBMamd3T1dNdE16SXVOVEU1TERFeUxqazJMVFV3TGpJeU15d3lPUzR5TURZdE5UQXVNakl6TERRMkxqTTVOSFkxTXk0NU1qUmpNQ3d4T1M0ek9ESXNNakl1TlRJc016Y3VOVFl6TERZekxqUXhNeXcxTVM0eE9UZ3NOREF1TnpZekxERXpMalU1TWl3NU5DNDVOVFFzTWpFdU1EYzVMREUxTWk0Mk1ESXNNakV1TURjNWN6RXhNUzQ0TXpFdE55NDBPRGNzTVRVeUxqVTROeTB5TVM0d056bGpOREF1T0RnMkxURXpMall6Tml3Mk15NHpPVGd0TXpFdU9ERTJMRFl6TGpNNU9DMDFNUzR4T1RoMkxUVXpMamt5TkdNd0xURTNMakU1TmkweE55NDNNRFF0TXpNdU5ETTFMVFV3TGpJeU15MDBOaTQwTURGV01qQTNMall3TTJNek1pNDFNVGt0TVRJdU9UWTNMRFV3TGpJeU15MHlPUzR5TURZc05UQXVNakl6TFRRMkxqUXdNVnB0TFRNME55NDBOaklzTlRjdU56a3piREV6TUM0NU5Ua3NNVE14TGpBeU55MHhNekF1T1RVNUxERXpNUzR3TVROV01qRTRMams1TkZwdE1qWXlMamt5TkM0d01qSjJNall5TGpBeE9Hd3RNVE13TGprek55MHhNekV1TURBMkxERXpNQzQ1TXpjdE1UTXhMakF4TTFvaUlHWnBiR3c5SWlNeE5qRTRNaklpUGp3dmNHRjBhRDQ4Y0c5c2VXZHZiaUJ3YjJsdWRITTlJak0xTUNBek5UQXVNREkySURReE5TNHdNeUF5T0RRdU9UYzRJREk0TlNBeU9EUXVPVGM0SURNMU1DQXpOVEF1TURJMklpQm1hV3hzUFNKMWNtd29JMU5oYm1SQ2IzUjBiMjBwSWk4K1BIQmhkR2dnWkQwaWJUUXhOaTR6TkRFc01qZ3hMamszTldNd0xDNDVNVFF0TGpNMU5Dd3hMamd3T1MweExqQXpOU3d5TGpZNExUVXVOVFF5TERjdU1EYzJMVE15TGpZMk1Td3hNaTQwTlMwMk5TNHlPQ3d4TWk0ME5TMHpNaTQyTWpRc01DMDFPUzQzTXpndE5TNHpOelF0TmpVdU1qZ3RNVEl1TkRVdExqWTRNUzB1T0RjeUxURXVNRE0xTFRFdU56WTNMVEV1TURNMUxUSXVOamdzTUMwdU9URTBMak0xTkMweExqZ3dPQ3d4TGpBek5TMHlMalkzTml3MUxqVTBNaTAzTGpBM05pd3pNaTQyTlRZdE1USXVORFVzTmpVdU1qZ3RNVEl1TkRVc016SXVOakU1TERBc05Ua3VOek00TERVdU16YzBMRFkxTGpJNExERXlMalExTGpZNE1TNDROamNzTVM0d016VXNNUzQzTmpJc01TNHdNelVzTWk0Mk56WmFJaUJtYVd4c1BTSjFjbXdvSTFOaGJtUlViM0FwSWk4K1BIQmhkR2dnWkQwaWJUUTRNUzQwTml3MU1EUXVNVEF4ZGpVNExqUTBPV010TWk0ek5TNDNOeTAwTGpneUxERXVOVEV0Tnk0ek9Td3lMakl6TFRNd0xqTXNPQzQxTkMwM05DNDJOU3d4TXk0NU1pMHhNalF1TURZc01UTXVPVEl0TlRNdU5pd3dMVEV3TVM0eU5DMDJMak16TFRFek1TNDBOeTB4Tmk0eE5uWXROVGd1TkRNNWFESTJNaTQ1TWxvaUlHWnBiR3c5SW5WeWJDZ2pVMkZ1WkVKdmRIUnZiU2tpTHo0OFpXeHNhWEJ6WlNCamVEMGlNelV3SWlCamVUMGlOVEEwTGpFd01TSWdjbmc5SWpFek1TNDBOaklpSUhKNVBTSXlPQzR4TURnaUlHWnBiR3c5SW5WeWJDZ2pVMkZ1WkZSdmNDa2lMejQ4WnlCbWFXeHNQU0p1YjI1bElpQnpkSEp2YTJVOUluVnliQ2dqU0c5MWNtZHNZWE56VTNSeWIydGxLU0lnYzNSeWIydGxMV3hwYm1WallYQTlJbkp2ZFc1a0lpQnpkSEp2YTJVdGJXbDBaWEpzYVcxcGREMGlNVEFpSUhOMGNtOXJaUzEzYVdSMGFEMGlOQ0krUEhCaGRHZ2daRDBpYlRVMk5TNDJOREVzTVRBM0xqSTRZekFzT1M0MU16Y3ROUzQxTml3eE9DNDJNamt0TVRVdU5qYzJMREkyTGprM00yZ3RMakF5TTJNdE9TNHlNRFFzTnk0MU9UWXRNakl1TVRrMExERTBMalUyTWkwek9DNHhPVGNzTWpBdU5Ua3lMVE01TGpVd05Dd3hOQzQ1TXpZdE9UY3VNekkxTERJMExqTTFOUzB4TmpFdU56TXpMREkwTGpNMU5TMDVNQzQwT0N3d0xURTJOeTQ1TkRndE1UZ3VOVGd5TFRFNU9TNDVOVE10TkRRdU9UUTRhQzB1TURJell5MHhNQzR4TVRVdE9DNHpORFF0TVRVdU5qYzJMVEUzTGpRek55MHhOUzQyTnpZdE1qWXVPVGN6TERBdE16a3VOek0xTERrMkxqVTFOQzAzTVM0NU1qRXNNakUxTGpZMU1pMDNNUzQ1TWpGek1qRTFMall5T1N3ek1pNHhPRFVzTWpFMUxqWXlPU3czTVM0NU1qRmFJaTgrUEhCaGRHZ2daRDBpYlRFek5DNHpOaXd4TmpFdU1qQXpZekFzTXprdU56TTFMRGsyTGpVMU5DdzNNUzQ1TWpFc01qRTFMalkxTWl3M01TNDVNakZ6TWpFMUxqWXlPUzB6TWk0eE9EWXNNakUxTGpZeU9TMDNNUzQ1TWpFaUx6NDhiR2x1WlNCNE1UMGlNVE0wTGpNMklpQjVNVDBpTVRZeExqSXdNeUlnZURJOUlqRXpOQzR6TmlJZ2VUSTlJakV3Tnk0eU9DSXZQanhzYVc1bElIZ3hQU0kxTmpVdU5qUWlJSGt4UFNJeE5qRXVNakF6SWlCNE1qMGlOVFkxTGpZMElpQjVNajBpTVRBM0xqSTRJaTgrUEd4cGJtVWdlREU5SWpFNE5DNDFPRFFpSUhreFBTSXlNRFl1T0RJeklpQjRNajBpTVRnMExqVTROU0lnZVRJOUlqVXpOeTQxTnpraUx6NDhiR2x1WlNCNE1UMGlNakU0TGpFNE1TSWdlVEU5SWpJeE9DNHhNVGdpSUhneVBTSXlNVGd1TVRneElpQjVNajBpTlRZeUxqVXpOeUl2UGp4c2FXNWxJSGd4UFNJME9ERXVPREU0SWlCNU1UMGlNakU0TGpFME1pSWdlREk5SWpRNE1TNDRNVGtpSUhreVBTSTFOakl1TkRJNElpOCtQR3hwYm1VZ2VERTlJalV4TlM0ME1UVWlJSGt4UFNJeU1EY3VNelV5SWlCNE1qMGlOVEUxTGpReE5pSWdlVEk5SWpVek55NDFOemtpTHo0OGNHRjBhQ0JrUFNKdE1UZzBMalU0TERVek55NDFPR013TERVdU5EVXNOQzR5Tnl3eE1DNDJOU3d4TWk0d015d3hOUzQwTW1ndU1ESmpOUzQxTVN3ekxqTTVMREV5TGpjNUxEWXVOVFVzTWpFdU5UVXNPUzQwTWl3ek1DNHlNU3c1TGprc056Z3VNRElzTVRZdU1qZ3NNVE14TGpnekxERTJMakk0TERRNUxqUXhMREFzT1RNdU56WXROUzR6T0N3eE1qUXVNRFl0TVRNdU9USXNNaTQzTFM0M05pdzFMakk1TFRFdU5UUXNOeTQzTlMweUxqTTFMRGd1TnpjdE1pNDROeXd4Tmk0d05TMDJMakEwTERJeExqVTJMVGt1TkROb01HTTNMamMyTFRRdU56Y3NNVEl1TURRdE9TNDVOeXd4TWk0d05DMHhOUzQwTWlJdlBqeHdZWFJvSUdROUltMHhPRFF1TlRneUxEUTVNaTQyTlRaakxUTXhMak0xTkN3eE1pNDBPRFV0TlRBdU1qSXpMREk0TGpVNExUVXdMakl5TXl3ME5pNHhORElzTUN3NUxqVXpOaXcxTGpVMk5Dd3hPQzQyTWpjc01UVXVOamMzTERJMkxqazJPV2d1TURJeVl6Z3VOVEF6TERjdU1EQTFMREl3TGpJeE15d3hNeTQwTmpNc016UXVOVEkwTERFNUxqRTFPU3c1TGprNU9Td3pMams1TVN3eU1TNHlOamtzTnk0Mk1Ea3NNek11TlRrM0xERXdMamM0T0N3ek5pNDBOU3c1TGpRd055dzRNaTR4T0RFc01UVXVNREF5TERFek1TNDRNelVzTVRVdU1EQXljemsxTGpNMk15MDFMalU1TlN3eE16RXVPREEzTFRFMUxqQXdNbU14TUM0NE5EY3RNaTQzT1N3eU1DNDROamN0TlM0NU1qWXNNamt1T1RJMExUa3VNelE1TERFdU1qUTBMUzQwTmpjc01pNDBOek10TGprME1pd3pMalkzTXkweExqUXlOQ3d4TkM0ek1qWXROUzQyT1RZc01qWXVNRE0xTFRFeUxqRTJNU3d6TkM0MU1qUXRNVGt1TVRjemFDNHdNakpqTVRBdU1URTBMVGd1TXpReUxERTFMalkzTnkweE55NDBNek1zTVRVdU5qYzNMVEkyTGprMk9Td3dMVEUzTGpVMk1pMHhPQzQ0TmprdE16TXVOalkxTFRVd0xqSXlNeTAwTmk0eE5TSXZQanh3WVhSb0lHUTlJbTB4TXpRdU16WXNOVGt5TGpjeVl6QXNNemt1TnpNMUxEazJMalUxTkN3M01TNDVNakVzTWpFMUxqWTFNaXczTVM0NU1qRnpNakUxTGpZeU9TMHpNaTR4T0RZc01qRTFMall5T1MwM01TNDVNakVpTHo0OGJHbHVaU0I0TVQwaU1UTTBMak0ySWlCNU1UMGlOVGt5TGpjeUlpQjRNajBpTVRNMExqTTJJaUI1TWowaU5UTTRMamM1TnlJdlBqeHNhVzVsSUhneFBTSTFOalV1TmpRaUlIa3hQU0kxT1RJdU56SWlJSGd5UFNJMU5qVXVOalFpSUhreVBTSTFNemd1TnprM0lpOCtQSEJ2Ykhsc2FXNWxJSEJ2YVc1MGN6MGlORGd4TGpneU1pQTBPREV1T1RBeElEUTRNUzQzT1RnZ05EZ3hMamczTnlBME9ERXVOemMxSURRNE1TNDROVFFnTXpVd0xqQXhOU0F6TlRBdU1ESTJJREl4T0M0eE9EVWdNakU0TGpFeU9TSXZQanh3YjJ4NWJHbHVaU0J3YjJsdWRITTlJakl4T0M0eE9EVWdORGd4TGprd01TQXlNVGd1TWpNeElEUTRNUzQ0TlRRZ016VXdMakF4TlNBek5UQXVNREkySURRNE1TNDRNaklnTWpFNExqRTFNaUl2UGp3dlp6NDhMMmMrUEdjZ2FXUTlJbEJ5YjJkeVpYTnpJaUJtYVd4c1BTSWpabVptSWo0OGNtVmpkQ0IzYVdSMGFEMGlNakE0SWlCb1pXbG5hSFE5SWpFd01DSWdabWxzYkMxdmNHRmphWFI1UFNJdU1ETWlJSEo0UFNJeE5TSWdjbms5SWpFMUlpQnpkSEp2YTJVOUlpTm1abVlpSUhOMGNtOXJaUzF2Y0dGamFYUjVQU0l1TVNJZ2MzUnliMnRsTFhkcFpIUm9QU0kwSWk4K1BIUmxlSFFnZUQwaU1qQWlJSGs5SWpNMElpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5eEJjbWxoYkN4dGIyNXZjM0JoWTJVaUlHWnZiblF0YzJsNlpUMGlNakp3ZUNJK1VISnZaM0psYzNNOEwzUmxlSFErUEhSbGVIUWdlRDBpTWpBaUlIazlJamN5SWlCbWIyNTBMV1poYldsc2VUMGlKME52ZFhKcFpYSWdUbVYzSnl4QmNtbGhiQ3h0YjI1dmMzQmhZMlVpSUdadmJuUXRjMmw2WlQwaU1qWndlQ0krTWpVbFBDOTBaWGgwUGp4bklHWnBiR3c5SW01dmJtVWlQanhqYVhKamJHVWdZM2c5SWpFMk5pSWdZM2s5SWpVd0lpQnlQU0l5TWlJZ2MzUnliMnRsUFNKb2Myd29Nak13TERJeEpTd3hNU1VwSWlCemRISnZhMlV0ZDJsa2RHZzlJakV3SWk4K1BHTnBjbU5zWlNCamVEMGlNVFkySWlCamVUMGlOVEFpSUhCaGRHaE1aVzVuZEdnOUlqRXdNREF3SWlCeVBTSXlNaUlnYzNSeWIydGxQU0pvYzJ3b01Ua3NNaklsTERZekpTa2lJSE4wY205clpTMWtZWE5vWVhKeVlYazlJakV3TURBd0lpQnpkSEp2YTJVdFpHRnphRzltWm5ObGREMGlOelV3TUNJZ2MzUnliMnRsTFd4cGJtVmpZWEE5SW5KdmRXNWtJaUJ6ZEhKdmEyVXRkMmxrZEdnOUlqVWlJSFJ5WVc1elptOXliVDBpY205MFlYUmxLQzA1TUNraUlIUnlZVzV6Wm05eWJTMXZjbWxuYVc0OUlqRTJOaUExTUNJdlBqd3ZaejQ4TDJjK1BHY2dhV1E5SWxOMFlYUjFjeUlnWm1sc2JEMGlJMlptWmlJK1BISmxZM1FnZDJsa2RHZzlJakU0TkNJZ2FHVnBaMmgwUFNJeE1EQWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqQXpJaUJ5ZUQwaU1UVWlJSEo1UFNJeE5TSWdjM1J5YjJ0bFBTSWpabVptSWlCemRISnZhMlV0YjNCaFkybDBlVDBpTGpFaUlITjBjbTlyWlMxM2FXUjBhRDBpTkNJdlBqeDBaWGgwSUhnOUlqSXdJaUI1UFNJek5DSWdabTl1ZEMxbVlXMXBiSGs5SWlkRGIzVnlhV1Z5SUU1bGR5Y3NRWEpwWVd3c2JXOXViM053WVdObElpQm1iMjUwTFhOcGVtVTlJakl5Y0hnaVBsTjBZWFIxY3p3dmRHVjRkRDQ4ZEdWNGRDQjRQU0l5TUNJZ2VUMGlOeklpSUdadmJuUXRabUZ0YVd4NVBTSW5RMjkxY21sbGNpQk9aWGNuTEVGeWFXRnNMRzF2Ym05emNHRmpaU0lnWm05dWRDMXphWHBsUFNJeU5uQjRJajVUZEhKbFlXMXBibWM4TDNSbGVIUStQQzluUGp4bklHbGtQU0pCYlc5MWJuUWlJR1pwYkd3OUlpTm1abVlpUGp4eVpXTjBJSGRwWkhSb1BTSXhNakFpSUdobGFXZG9kRDBpTVRBd0lpQm1hV3hzTFc5d1lXTnBkSGs5SWk0d015SWdjbmc5SWpFMUlpQnllVDBpTVRVaUlITjBjbTlyWlQwaUkyWm1aaUlnYzNSeWIydGxMVzl3WVdOcGRIazlJaTR4SWlCemRISnZhMlV0ZDJsa2RHZzlJalFpTHo0OGRHVjRkQ0I0UFNJeU1DSWdlVDBpTXpRaUlHWnZiblF0Wm1GdGFXeDVQU0luUTI5MWNtbGxjaUJPWlhjbkxFRnlhV0ZzTEcxdmJtOXpjR0ZqWlNJZ1ptOXVkQzF6YVhwbFBTSXlNbkI0SWo1QmJXOTFiblE4TDNSbGVIUStQSFJsZUhRZ2VEMGlNakFpSUhrOUlqY3lJaUJtYjI1MExXWmhiV2xzZVQwaUowTnZkWEpwWlhJZ1RtVjNKeXhCY21saGJDeHRiMjV2YzNCaFkyVWlJR1p2Ym5RdGMybDZaVDBpTWpad2VDSStKaU00T0RBMU95QXhNRXM4TDNSbGVIUStQQzluUGp4bklHbGtQU0pFZFhKaGRHbHZiaUlnWm1sc2JEMGlJMlptWmlJK1BISmxZM1FnZDJsa2RHZzlJakUxTWlJZ2FHVnBaMmgwUFNJeE1EQWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqQXpJaUJ5ZUQwaU1UVWlJSEo1UFNJeE5TSWdjM1J5YjJ0bFBTSWpabVptSWlCemRISnZhMlV0YjNCaFkybDBlVDBpTGpFaUlITjBjbTlyWlMxM2FXUjBhRDBpTkNJdlBqeDBaWGgwSUhnOUlqSXdJaUI1UFNJek5DSWdabTl1ZEMxbVlXMXBiSGs5SWlkRGIzVnlhV1Z5SUU1bGR5Y3NRWEpwWVd3c2JXOXViM053WVdObElpQm1iMjUwTFhOcGVtVTlJakl5Y0hnaVBrUjFjbUYwYVc5dVBDOTBaWGgwUGp4MFpYaDBJSGc5SWpJd0lpQjVQU0kzTWlJZ1ptOXVkQzFtWVcxcGJIazlJaWREYjNWeWFXVnlJRTVsZHljc1FYSnBZV3dzYlc5dWIzTndZV05sSWlCbWIyNTBMWE5wZW1VOUlqSTJjSGdpUGlac2REc2dNU0JFWVhrOEwzUmxlSFErUEM5blBqd3ZaR1ZtY3o0OGRHVjRkQ0IwWlhoMExYSmxibVJsY21sdVp6MGliM0IwYVcxcGVtVlRjR1ZsWkNJK1BIUmxlSFJRWVhSb0lITjBZWEowVDJabWMyVjBQU0l0TVRBd0pTSWdhSEpsWmowaUkwWnNiMkYwYVc1blZHVjRkQ0lnWm1sc2JEMGlJMlptWmlJZ1ptOXVkQzFtWVcxcGJIazlJaWREYjNWeWFXVnlJRTVsZHljc1FYSnBZV3dzYlc5dWIzTndZV05sSWlCbWFXeHNMVzl3WVdOcGRIazlJaTQ0SWlCbWIyNTBMWE5wZW1VOUlqSTJjSGdpUGp4aGJtbHRZWFJsSUdGa1pHbDBhWFpsUFNKemRXMGlJR0YwZEhKcFluVjBaVTVoYldVOUluTjBZWEowVDJabWMyVjBJaUJpWldkcGJqMGlNSE1pSUdSMWNqMGlOVEJ6SWlCbWNtOXRQU0l3SlNJZ2NtVndaV0YwUTI5MWJuUTlJbWx1WkdWbWFXNXBkR1VpSUhSdlBTSXhNREFsSWk4K01IZ3pNemd4WTJReE9HVXlabUkwWkdJeU16WmlaakExTWpVNU16aGhZalpsTkROa1lqQTBOREJtSU9LQW9pQlRZV0pzYVdWeUlGWXlJRXh2WTJ0MWNDQk1hVzVsWVhJOEwzUmxlSFJRWVhSb1BqeDBaWGgwVUdGMGFDQnpkR0Z5ZEU5bVpuTmxkRDBpTUNVaUlHaHlaV1k5SWlOR2JHOWhkR2x1WjFSbGVIUWlJR1pwYkd3OUlpTm1abVlpSUdadmJuUXRabUZ0YVd4NVBTSW5RMjkxY21sbGNpQk9aWGNuTEVGeWFXRnNMRzF2Ym05emNHRmpaU0lnWm1sc2JDMXZjR0ZqYVhSNVBTSXVPQ0lnWm05dWRDMXphWHBsUFNJeU5uQjRJajQ4WVc1cGJXRjBaU0JoWkdScGRHbDJaVDBpYzNWdElpQmhkSFJ5YVdKMWRHVk9ZVzFsUFNKemRHRnlkRTltWm5ObGRDSWdZbVZuYVc0OUlqQnpJaUJrZFhJOUlqVXdjeUlnWm5KdmJUMGlNQ1VpSUhKbGNHVmhkRU52ZFc1MFBTSnBibVJsWm1sdWFYUmxJaUIwYnowaU1UQXdKU0l2UGpCNE16TTRNV05rTVRobE1tWmlOR1JpTWpNMlltWXdOVEkxT1RNNFlXSTJaVFF6WkdJd05EUXdaaURpZ0tJZ1UyRmliR2xsY2lCV01pQk1iMk5yZFhBZ1RHbHVaV0Z5UEM5MFpYaDBVR0YwYUQ0OGRHVjRkRkJoZEdnZ2MzUmhjblJQWm1aelpYUTlJaTAxTUNVaUlHaHlaV1k5SWlOR2JHOWhkR2x1WjFSbGVIUWlJR1pwYkd3OUlpTm1abVlpSUdadmJuUXRabUZ0YVd4NVBTSW5RMjkxY21sbGNpQk9aWGNuTEVGeWFXRnNMRzF2Ym05emNHRmpaU0lnWm1sc2JDMXZjR0ZqYVhSNVBTSXVPQ0lnWm05dWRDMXphWHBsUFNJeU5uQjRJajQ4WVc1cGJXRjBaU0JoWkdScGRHbDJaVDBpYzNWdElpQmhkSFJ5YVdKMWRHVk9ZVzFsUFNKemRHRnlkRTltWm5ObGRDSWdZbVZuYVc0OUlqQnpJaUJrZFhJOUlqVXdjeUlnWm5KdmJUMGlNQ1VpSUhKbGNHVmhkRU52ZFc1MFBTSnBibVJsWm1sdWFYUmxJaUIwYnowaU1UQXdKU0l2UGpCNE1ETmhObUU0TkdOa056WXlaRGszTURkaE1qRTJNRFZpTlRRNFlXRmhZamc1TVRVMk1tRmhZaURpZ0tJZ1JFRkpQQzkwWlhoMFVHRjBhRDQ4ZEdWNGRGQmhkR2dnYzNSaGNuUlBabVp6WlhROUlqVXdKU0lnYUhKbFpqMGlJMFpzYjJGMGFXNW5WR1Y0ZENJZ1ptbHNiRDBpSTJabVppSWdabTl1ZEMxbVlXMXBiSGs5SWlkRGIzVnlhV1Z5SUU1bGR5Y3NRWEpwWVd3c2JXOXViM053WVdObElpQm1hV3hzTFc5d1lXTnBkSGs5SWk0NElpQm1iMjUwTFhOcGVtVTlJakkyY0hnaVBqeGhibWx0WVhSbElHRmtaR2wwYVhabFBTSnpkVzBpSUdGMGRISnBZblYwWlU1aGJXVTlJbk4wWVhKMFQyWm1jMlYwSWlCaVpXZHBiajBpTUhNaUlHUjFjajBpTlRCeklpQm1jbTl0UFNJd0pTSWdjbVZ3WldGMFEyOTFiblE5SW1sdVpHVm1hVzVwZEdVaUlIUnZQU0l4TURBbElpOCtNSGd3TTJFMllUZzBZMlEzTmpKa09UY3dOMkV5TVRZd05XSTFORGhoWVdGaU9Ea3hOVFl5WVdGaUlPS0FvaUJFUVVrOEwzUmxlSFJRWVhSb1Bqd3ZkR1Y0ZEQ0OGRYTmxJR2h5WldZOUlpTkhiRzkzSWlCbWFXeHNMVzl3WVdOcGRIazlJaTQ1SWk4K1BIVnpaU0JvY21WbVBTSWpSMnh2ZHlJZ2VEMGlNVEF3TUNJZ2VUMGlNVEF3TUNJZ1ptbHNiQzF2Y0dGamFYUjVQU0l1T1NJdlBqeDFjMlVnYUhKbFpqMGlJMHh2WjI4aUlIZzlJakUzTUNJZ2VUMGlNVGN3SWlCMGNtRnVjMlp2Y20wOUluTmpZV3hsS0M0MktTSXZQangxYzJVZ2FISmxaajBpSTBodmRYSm5iR0Z6Y3lJZ2VEMGlNVFV3SWlCNVBTSTVNQ0lnZEhKaGJuTm1iM0p0UFNKeWIzUmhkR1VvTVRBcElpQjBjbUZ1YzJadmNtMHRiM0pwWjJsdVBTSTFNREFnTlRBd0lpOCtQSFZ6WlNCb2NtVm1QU0lqVUhKdlozSmxjM01pSUhnOUlqRTBOQ0lnZVQwaU56a3dJaTgrUEhWelpTQm9jbVZtUFNJalUzUmhkSFZ6SWlCNFBTSXpOamdpSUhrOUlqYzVNQ0l2UGp4MWMyVWdhSEpsWmowaUkwRnRiM1Z1ZENJZ2VEMGlOVFk0SWlCNVBTSTNPVEFpTHo0OGRYTmxJR2h5WldZOUlpTkVkWEpoZEdsdmJpSWdlRDBpTnpBMElpQjVQU0kzT1RBaUx6NDhMM04yWno0PSJ9"; - assertEq(actualTokenURI, expectedTokenURI, "token URI"); - } -} diff --git a/tests/integration/concrete/lockup-tranched/token-uri/tokenURI.t.sol b/tests/integration/concrete/lockup-tranched/token-uri/tokenURI.t.sol deleted file mode 100644 index eef12008b..000000000 --- a/tests/integration/concrete/lockup-tranched/token-uri/tokenURI.t.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -// solhint-disable max-line-length,no-console,quotes -pragma solidity >=0.8.22 <0.9.0; - -import { IERC721Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; -import { console2 } from "forge-std/src/console2.sol"; -import { StdStyle } from "forge-std/src/StdStyle.sol"; -import { Base64 } from "solady/src/utils/Base64.sol"; - -import { Integration_Test } from "../LockupTranched.t.sol"; - -/// @dev Requirements for these tests to work: -/// - The stream ID must be 1 -/// - The stream's sender must be `0x6332e7b1deb1f1a0b77b2bb18b144330c7291bca`, i.e. `makeAddr("Sender")` -/// - The stream token must have the DAI symbol -/// - The contract deployer, i.e. the `sender` config option in `foundry.toml`, must have the default value -/// 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38 so that the deployed contracts have the same addresses as -/// the values hard coded in the tests below -contract TokenURI_Lockup_Tranched_Integration_Concrete_Test is Integration_Test { - address internal constant LOCKUP = 0xDB25A7b768311dE128BBDa7B8426c3f9C74f3240; - - /// @dev To make these tests noninvasive, they are run only when the contract address matches the hard coded value. - modifier skipOnMismatch() { - if (address(lockup) == LOCKUP) { - _; - } else { - console2.log(StdStyle.yellow('Warning: "lockup.tokenURI" tests skipped due to address mismatch')); - } - } - - function test_RevertGiven_NFTNotExist() external { - vm.expectRevert(abi.encodeWithSelector(IERC721Errors.ERC721NonexistentToken.selector, nullStreamId)); - lockup.tokenURI({ tokenId: nullStreamId }); - } - - /// @dev If you need to update the hard-coded token URI: - /// 1. Use "vm.writeFile" to log the strings to a file. - /// 2. Remember to escape the EOL character \n with \\n. - function test_WhenTokenURIDecoded() external skipOnMismatch givenNFTExists { - vm.warp({ newTimestamp: defaults.START_TIME() + defaults.TOTAL_DURATION() / 4 }); - - string memory tokenURI = lockup.tokenURI(defaultStreamId); - 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":"DAI"},{"trait_type":"Sender","value":"0x6332e7b1deb1f1a0b77b2bb18b144330c7291bca"},{"trait_type":"Status","value":"Streaming"}],"description":"This NFT represents a payment stream in a Sablier Lockup Tranched contract. The owner of this NFT can withdraw the streamed tokens, which are denominated in DAI.\\n\\n- Stream ID: 1\\n- Lockup Tranched Address: 0xdb25a7b768311de128bbda7b8426c3f9c74f3240\\n- DAI Address: 0x03a6a84cd762d9707a21605b548aaab891562aab\\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 Tranched #1","image":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAwIiBoZWlnaHQ9IjEwMDAiIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbHRlcj0idXJsKCNOb2lzZSkiLz48cmVjdCB4PSI3MCIgeT0iNzAiIHdpZHRoPSI4NjAiIGhlaWdodD0iODYwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4wMyIgcng9IjQ1IiByeT0iNDUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLW9wYWNpdHk9Ii4xIiBzdHJva2Utd2lkdGg9IjQiLz48ZGVmcz48Y2lyY2xlIGlkPSJHbG93IiByPSI1MDAiIGZpbGw9InVybCgjUmFkaWFsR2xvdykiLz48ZmlsdGVyIGlkPSJOb2lzZSI+PGZlRmxvb2QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmxvb2QtY29sb3I9ImhzbCgyMzAsMjElLDExJSkiIGZsb29kLW9wYWNpdHk9IjEiIHJlc3VsdD0iZmxvb2RGaWxsIi8+PGZlVHVyYnVsZW5jZSBiYXNlRnJlcXVlbmN5PSIuNCIgbnVtT2N0YXZlcz0iMyIgcmVzdWx0PSJOb2lzZSIgdHlwZT0iZnJhY3RhbE5vaXNlIi8+PGZlQmxlbmQgaW49Ik5vaXNlIiBpbjI9ImZsb29kRmlsbCIgbW9kZT0ic29mdC1saWdodCIvPjwvZmlsdGVyPjxwYXRoIGlkPSJMb2dvIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4xIiBkPSJtMTMzLjU1OSwxMjQuMDM0Yy0uMDEzLDIuNDEyLTEuMDU5LDQuODQ4LTIuOTIzLDYuNDAyLTIuNTU4LDEuODE5LTUuMTY4LDMuNDM5LTcuODg4LDQuOTk2LTE0LjQ0LDguMjYyLTMxLjA0NywxMi41NjUtNDcuNjc0LDEyLjU2OS04Ljg1OC4wMzYtMTcuODM4LTEuMjcyLTI2LjMyOC0zLjY2My05LjgwNi0yLjc2Ni0xOS4wODctNy4xMTMtMjcuNTYyLTEyLjc3OC0xMy44NDItOC4wMjUsOS40NjgtMjguNjA2LDE2LjE1My0zNS4yNjVoMGMyLjAzNS0xLjgzOCw0LjI1Mi0zLjU0Niw2LjQ2My01LjIyNGgwYzYuNDI5LTUuNjU1LDE2LjIxOC0yLjgzNSwyMC4zNTgsNC4xNyw0LjE0Myw1LjA1Nyw4LjgxNiw5LjY0OSwxMy45MiwxMy43MzRoLjAzN2M1LjczNiw2LjQ2MSwxNS4zNTctMi4yNTMsOS4zOC04LjQ4LDAsMC0zLjUxNS0zLjUxNS0zLjUxNS0zLjUxNS0xMS40OS0xMS40NzgtNTIuNjU2LTUyLjY2NC02NC44MzctNjQuODM3bC4wNDktLjAzN2MtMS43MjUtMS42MDYtMi43MTktMy44NDctMi43NTEtNi4yMDRoMGMtLjA0Ni0yLjM3NSwxLjA2Mi00LjU4MiwyLjcyNi02LjIyOWgwbC4xODUtLjE0OGgwYy4wOTktLjA2MiwuMjIyLS4xNDgsLjM3LS4yNTloMGMyLjA2LTEuMzYyLDMuOTUxLTIuNjIxLDYuMDQ0LTMuODQyQzU3Ljc2My0zLjQ3Myw5Ny43Ni0yLjM0MSwxMjguNjM3LDE4LjMzMmMxNi42NzEsOS45NDYtMjYuMzQ0LDU0LjgxMy0zOC42NTEsNDAuMTk5LTYuMjk5LTYuMDk2LTE4LjA2My0xNy43NDMtMTkuNjY4LTE4LjgxMS02LjAxNi00LjA0Ny0xMy4wNjEsNC43NzYtNy43NTIsOS43NTFsNjguMjU0LDY4LjM3MWMxLjcyNCwxLjYwMSwyLjcxNCwzLjg0LDIuNzM4LDYuMTkyWiIvPjxwYXRoIGlkPSJGbG9hdGluZ1RleHQiIGZpbGw9Im5vbmUiIGQ9Ik0xMjUgNDVoNzUwczgwIDAgODAgODB2NzUwczAgODAgLTgwIDgwaC03NTBzLTgwIDAgLTgwIC04MHYtNzUwczAgLTgwIDgwIC04MCIvPjxyYWRpYWxHcmFkaWVudCBpZD0iUmFkaWFsR2xvdyI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iaHNsKDYxLDg4JSw0MCUpIiBzdG9wLW9wYWNpdHk9Ii42Ii8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSJoc2woMjMwLDIxJSwxMSUpIiBzdG9wLW9wYWNpdHk9IjAiLz48L3JhZGlhbEdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iU2FuZFRvcCIgeDE9IjAlIiB5MT0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9ImhzbCg2MSw4OCUsNDAlKSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iaHNsKDIzMCwyMSUsMTElKSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJTYW5kQm90dG9tIiB4MT0iMTAwJSIgeTE9IjEwMCUiPjxzdG9wIG9mZnNldD0iMTAlIiBzdG9wLWNvbG9yPSJoc2woMjMwLDIxJSwxMSUpIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSJoc2woNjEsODglLDQwJSkiLz48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ4MSIgZHVyPSI2cyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHZhbHVlcz0iMzAlOzYwJTsxMjAlOzYwJTszMCU7Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9IkhvdXJnbGFzc1N0cm9rZSIgZ3JhZGllbnRUcmFuc2Zvcm09InJvdGF0ZSg5MCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iaHNsKDYxLDg4JSw0MCUpIi8+PHN0b3Agb2Zmc2V0PSI4MCUiIHN0b3AtY29sb3I9ImhzbCgyMzAsMjElLDExJSkiLz48L2xpbmVhckdyYWRpZW50PjxnIGlkPSJIb3VyZ2xhc3MiPjxwYXRoIGQ9Ik0gNTAsMzYwIGEgMzAwLDMwMCAwIDEsMSA2MDAsMCBhIDMwMCwzMDAgMCAxLDEgLTYwMCwwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4wMiIgc3Ryb2tlPSJ1cmwoI0hvdXJnbGFzc1N0cm9rZSkiIHN0cm9rZS13aWR0aD0iNCIvPjxwYXRoIGQ9Im01NjYsMTYxLjIwMXYtNTMuOTI0YzAtMTkuMzgyLTIyLjUxMy0zNy41NjMtNjMuMzk4LTUxLjE5OC00MC43NTYtMTMuNTkyLTk0Ljk0Ni0yMS4wNzktMTUyLjU4Ny0yMS4wNzlzLTExMS44MzgsNy40ODctMTUyLjYwMiwyMS4wNzljLTQwLjg5MywxMy42MzYtNjMuNDEzLDMxLjgxNi02My40MTMsNTEuMTk4djUzLjkyNGMwLDE3LjE4MSwxNy43MDQsMzMuNDI3LDUwLjIyMyw0Ni4zOTR2Mjg0LjgwOWMtMzIuNTE5LDEyLjk2LTUwLjIyMywyOS4yMDYtNTAuMjIzLDQ2LjM5NHY1My45MjRjMCwxOS4zODIsMjIuNTIsMzcuNTYzLDYzLjQxMyw1MS4xOTgsNDAuNzYzLDEzLjU5Miw5NC45NTQsMjEuMDc5LDE1Mi42MDIsMjEuMDc5czExMS44MzEtNy40ODcsMTUyLjU4Ny0yMS4wNzljNDAuODg2LTEzLjYzNiw2My4zOTgtMzEuODE2LDYzLjM5OC01MS4xOTh2LTUzLjkyNGMwLTE3LjE5Ni0xNy43MDQtMzMuNDM1LTUwLjIyMy00Ni40MDFWMjA3LjYwM2MzMi41MTktMTIuOTY3LDUwLjIyMy0yOS4yMDYsNTAuMjIzLTQ2LjQwMVptLTM0Ny40NjIsNTcuNzkzbDEzMC45NTksMTMxLjAyNy0xMzAuOTU5LDEzMS4wMTNWMjE4Ljk5NFptMjYyLjkyNC4wMjJ2MjYyLjAxOGwtMTMwLjkzNy0xMzEuMDA2LDEzMC45MzctMTMxLjAxM1oiIGZpbGw9IiMxNjE4MjIiPjwvcGF0aD48cG9seWdvbiBwb2ludHM9IjM1MCAzNTAuMDI2IDQxNS4wMyAyODQuOTc4IDI4NSAyODQuOTc4IDM1MCAzNTAuMDI2IiBmaWxsPSJ1cmwoI1NhbmRCb3R0b20pIi8+PHBhdGggZD0ibTQxNi4zNDEsMjgxLjk3NWMwLC45MTQtLjM1NCwxLjgwOS0xLjAzNSwyLjY4LTUuNTQyLDcuMDc2LTMyLjY2MSwxMi40NS02NS4yOCwxMi40NS0zMi42MjQsMC01OS43MzgtNS4zNzQtNjUuMjgtMTIuNDUtLjY4MS0uODcyLTEuMDM1LTEuNzY3LTEuMDM1LTIuNjgsMC0uOTE0LjM1NC0xLjgwOCwxLjAzNS0yLjY3Niw1LjU0Mi03LjA3NiwzMi42NTYtMTIuNDUsNjUuMjgtMTIuNDUsMzIuNjE5LDAsNTkuNzM4LDUuMzc0LDY1LjI4LDEyLjQ1LjY4MS44NjcsMS4wMzUsMS43NjIsMS4wMzUsMi42NzZaIiBmaWxsPSJ1cmwoI1NhbmRUb3ApIi8+PHBhdGggZD0ibTQ4MS40Niw1MDQuMTAxdjU4LjQ0OWMtMi4zNS43Ny00LjgyLDEuNTEtNy4zOSwyLjIzLTMwLjMsOC41NC03NC42NSwxMy45Mi0xMjQuMDYsMTMuOTItNTMuNiwwLTEwMS4yNC02LjMzLTEzMS40Ny0xNi4xNnYtNTguNDM5aDI2Mi45MloiIGZpbGw9InVybCgjU2FuZEJvdHRvbSkiLz48ZWxsaXBzZSBjeD0iMzUwIiBjeT0iNTA0LjEwMSIgcng9IjEzMS40NjIiIHJ5PSIyOC4xMDgiIGZpbGw9InVybCgjU2FuZFRvcCkiLz48ZyBmaWxsPSJub25lIiBzdHJva2U9InVybCgjSG91cmdsYXNzU3Ryb2tlKSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iNCI+PHBhdGggZD0ibTU2NS42NDEsMTA3LjI4YzAsOS41MzctNS41NiwxOC42MjktMTUuNjc2LDI2Ljk3M2gtLjAyM2MtOS4yMDQsNy41OTYtMjIuMTk0LDE0LjU2Mi0zOC4xOTcsMjAuNTkyLTM5LjUwNCwxNC45MzYtOTcuMzI1LDI0LjM1NS0xNjEuNzMzLDI0LjM1NS05MC40OCwwLTE2Ny45NDgtMTguNTgyLTE5OS45NTMtNDQuOTQ4aC0uMDIzYy0xMC4xMTUtOC4zNDQtMTUuNjc2LTE3LjQzNy0xNS42NzYtMjYuOTczLDAtMzkuNzM1LDk2LjU1NC03MS45MjEsMjE1LjY1Mi03MS45MjFzMjE1LjYyOSwzMi4xODUsMjE1LjYyOSw3MS45MjFaIi8+PHBhdGggZD0ibTEzNC4zNiwxNjEuMjAzYzAsMzkuNzM1LDk2LjU1NCw3MS45MjEsMjE1LjY1Miw3MS45MjFzMjE1LjYyOS0zMi4xODYsMjE1LjYyOS03MS45MjEiLz48bGluZSB4MT0iMTM0LjM2IiB5MT0iMTYxLjIwMyIgeDI9IjEzNC4zNiIgeTI9IjEwNy4yOCIvPjxsaW5lIHgxPSI1NjUuNjQiIHkxPSIxNjEuMjAzIiB4Mj0iNTY1LjY0IiB5Mj0iMTA3LjI4Ii8+PGxpbmUgeDE9IjE4NC41ODQiIHkxPSIyMDYuODIzIiB4Mj0iMTg0LjU4NSIgeTI9IjUzNy41NzkiLz48bGluZSB4MT0iMjE4LjE4MSIgeTE9IjIxOC4xMTgiIHgyPSIyMTguMTgxIiB5Mj0iNTYyLjUzNyIvPjxsaW5lIHgxPSI0ODEuODE4IiB5MT0iMjE4LjE0MiIgeDI9IjQ4MS44MTkiIHkyPSI1NjIuNDI4Ii8+PGxpbmUgeDE9IjUxNS40MTUiIHkxPSIyMDcuMzUyIiB4Mj0iNTE1LjQxNiIgeTI9IjUzNy41NzkiLz48cGF0aCBkPSJtMTg0LjU4LDUzNy41OGMwLDUuNDUsNC4yNywxMC42NSwxMi4wMywxNS40MmguMDJjNS41MSwzLjM5LDEyLjc5LDYuNTUsMjEuNTUsOS40MiwzMC4yMSw5LjksNzguMDIsMTYuMjgsMTMxLjgzLDE2LjI4LDQ5LjQxLDAsOTMuNzYtNS4zOCwxMjQuMDYtMTMuOTIsMi43LS43Niw1LjI5LTEuNTQsNy43NS0yLjM1LDguNzctMi44NywxNi4wNS02LjA0LDIxLjU2LTkuNDNoMGM3Ljc2LTQuNzcsMTIuMDQtOS45NywxMi4wNC0xNS40MiIvPjxwYXRoIGQ9Im0xODQuNTgyLDQ5Mi42NTZjLTMxLjM1NCwxMi40ODUtNTAuMjIzLDI4LjU4LTUwLjIyMyw0Ni4xNDIsMCw5LjUzNiw1LjU2NCwxOC42MjcsMTUuNjc3LDI2Ljk2OWguMDIyYzguNTAzLDcuMDA1LDIwLjIxMywxMy40NjMsMzQuNTI0LDE5LjE1OSw5Ljk5OSwzLjk5MSwyMS4yNjksNy42MDksMzMuNTk3LDEwLjc4OCwzNi40NSw5LjQwNyw4Mi4xODEsMTUuMDAyLDEzMS44MzUsMTUuMDAyczk1LjM2My01LjU5NSwxMzEuODA3LTE1LjAwMmMxMC44NDctMi43OSwyMC44NjctNS45MjYsMjkuOTI0LTkuMzQ5LDEuMjQ0LS40NjcsMi40NzMtLjk0MiwzLjY3My0xLjQyNCwxNC4zMjYtNS42OTYsMjYuMDM1LTEyLjE2MSwzNC41MjQtMTkuMTczaC4wMjJjMTAuMTE0LTguMzQyLDE1LjY3Ny0xNy40MzMsMTUuNjc3LTI2Ljk2OSwwLTE3LjU2Mi0xOC44NjktMzMuNjY1LTUwLjIyMy00Ni4xNSIvPjxwYXRoIGQ9Im0xMzQuMzYsNTkyLjcyYzAsMzkuNzM1LDk2LjU1NCw3MS45MjEsMjE1LjY1Miw3MS45MjFzMjE1LjYyOS0zMi4xODYsMjE1LjYyOS03MS45MjEiLz48bGluZSB4MT0iMTM0LjM2IiB5MT0iNTkyLjcyIiB4Mj0iMTM0LjM2IiB5Mj0iNTM4Ljc5NyIvPjxsaW5lIHgxPSI1NjUuNjQiIHkxPSI1OTIuNzIiIHgyPSI1NjUuNjQiIHkyPSI1MzguNzk3Ii8+PHBvbHlsaW5lIHBvaW50cz0iNDgxLjgyMiA0ODEuOTAxIDQ4MS43OTggNDgxLjg3NyA0ODEuNzc1IDQ4MS44NTQgMzUwLjAxNSAzNTAuMDI2IDIxOC4xODUgMjE4LjEyOSIvPjxwb2x5bGluZSBwb2ludHM9IjIxOC4xODUgNDgxLjkwMSAyMTguMjMxIDQ4MS44NTQgMzUwLjAxNSAzNTAuMDI2IDQ4MS44MjIgMjE4LjE1MiIvPjwvZz48L2c+PGcgaWQ9IlByb2dyZXNzIiBmaWxsPSIjZmZmIj48cmVjdCB3aWR0aD0iMjA4IiBoZWlnaHQ9IjEwMCIgZmlsbC1vcGFjaXR5PSIuMDMiIHJ4PSIxNSIgcnk9IjE1IiBzdHJva2U9IiNmZmYiIHN0cm9rZS1vcGFjaXR5PSIuMSIgc3Ryb2tlLXdpZHRoPSI0Ii8+PHRleHQgeD0iMjAiIHk9IjM0IiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjJweCI+UHJvZ3Jlc3M8L3RleHQ+PHRleHQgeD0iMjAiIHk9IjcyIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjZweCI+MjUlPC90ZXh0PjxnIGZpbGw9Im5vbmUiPjxjaXJjbGUgY3g9IjE2NiIgY3k9IjUwIiByPSIyMiIgc3Ryb2tlPSJoc2woMjMwLDIxJSwxMSUpIiBzdHJva2Utd2lkdGg9IjEwIi8+PGNpcmNsZSBjeD0iMTY2IiBjeT0iNTAiIHBhdGhMZW5ndGg9IjEwMDAwIiByPSIyMiIgc3Ryb2tlPSJoc2woNjEsODglLDQwJSkiIHN0cm9rZS1kYXNoYXJyYXk9IjEwMDAwIiBzdHJva2UtZGFzaG9mZnNldD0iNzUwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjUiIHRyYW5zZm9ybT0icm90YXRlKC05MCkiIHRyYW5zZm9ybS1vcmlnaW49IjE2NiA1MCIvPjwvZz48L2c+PGcgaWQ9IlN0YXR1cyIgZmlsbD0iI2ZmZiI+PHJlY3Qgd2lkdGg9IjE4NCIgaGVpZ2h0PSIxMDAiIGZpbGwtb3BhY2l0eT0iLjAzIiByeD0iMTUiIHJ5PSIxNSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZS13aWR0aD0iNCIvPjx0ZXh0IHg9IjIwIiB5PSIzNCIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjIycHgiPlN0YXR1czwvdGV4dD48dGV4dCB4PSIyMCIgeT0iNzIiIGZvbnQtZmFtaWx5PSInQ291cmllciBOZXcnLEFyaWFsLG1vbm9zcGFjZSIgZm9udC1zaXplPSIyNnB4Ij5TdHJlYW1pbmc8L3RleHQ+PC9nPjxnIGlkPSJBbW91bnQiIGZpbGw9IiNmZmYiPjxyZWN0IHdpZHRoPSIxMjAiIGhlaWdodD0iMTAwIiBmaWxsLW9wYWNpdHk9Ii4wMyIgcng9IjE1IiByeT0iMTUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLW9wYWNpdHk9Ii4xIiBzdHJva2Utd2lkdGg9IjQiLz48dGV4dCB4PSIyMCIgeT0iMzQiIGZvbnQtZmFtaWx5PSInQ291cmllciBOZXcnLEFyaWFsLG1vbm9zcGFjZSIgZm9udC1zaXplPSIyMnB4Ij5BbW91bnQ8L3RleHQ+PHRleHQgeD0iMjAiIHk9IjcyIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjZweCI+JiM4ODA1OyAxMEs8L3RleHQ+PC9nPjxnIGlkPSJEdXJhdGlvbiIgZmlsbD0iI2ZmZiI+PHJlY3Qgd2lkdGg9IjE1MiIgaGVpZ2h0PSIxMDAiIGZpbGwtb3BhY2l0eT0iLjAzIiByeD0iMTUiIHJ5PSIxNSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZS13aWR0aD0iNCIvPjx0ZXh0IHg9IjIwIiB5PSIzNCIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjIycHgiPkR1cmF0aW9uPC90ZXh0Pjx0ZXh0IHg9IjIwIiB5PSI3MiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmb250LXNpemU9IjI2cHgiPiZsdDsgMSBEYXk8L3RleHQ+PC9nPjwvZGVmcz48dGV4dCB0ZXh0LXJlbmRlcmluZz0ib3B0aW1pemVTcGVlZCI+PHRleHRQYXRoIHN0YXJ0T2Zmc2V0PSItMTAwJSIgaHJlZj0iI0Zsb2F0aW5nVGV4dCIgZmlsbD0iI2ZmZiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmaWxsLW9wYWNpdHk9Ii44IiBmb250LXNpemU9IjI2cHgiPjxhbmltYXRlIGFkZGl0aXZlPSJzdW0iIGF0dHJpYnV0ZU5hbWU9InN0YXJ0T2Zmc2V0IiBiZWdpbj0iMHMiIGR1cj0iNTBzIiBmcm9tPSIwJSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHRvPSIxMDAlIi8+MHhkYjI1YTdiNzY4MzExZGUxMjhiYmRhN2I4NDI2YzNmOWM3NGYzMjQwIOKAoiBTYWJsaWVyIFYyIExvY2t1cCBEeW5hbWljPC90ZXh0UGF0aD48dGV4dFBhdGggc3RhcnRPZmZzZXQ9IjAlIiBocmVmPSIjRmxvYXRpbmdUZXh0IiBmaWxsPSIjZmZmIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZpbGwtb3BhY2l0eT0iLjgiIGZvbnQtc2l6ZT0iMjZweCI+PGFuaW1hdGUgYWRkaXRpdmU9InN1bSIgYXR0cmlidXRlTmFtZT0ic3RhcnRPZmZzZXQiIGJlZ2luPSIwcyIgZHVyPSI1MHMiIGZyb209IjAlIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgdG89IjEwMCUiLz4weGRiMjVhN2I3NjgzMTFkZTEyOGJiZGE3Yjg0MjZjM2Y5Yzc0ZjMyNDAg4oCiIFNhYmxpZXIgVjIgTG9ja3VwIER5bmFtaWM8L3RleHRQYXRoPjx0ZXh0UGF0aCBzdGFydE9mZnNldD0iLTUwJSIgaHJlZj0iI0Zsb2F0aW5nVGV4dCIgZmlsbD0iI2ZmZiIgZm9udC1mYW1pbHk9IidDb3VyaWVyIE5ldycsQXJpYWwsbW9ub3NwYWNlIiBmaWxsLW9wYWNpdHk9Ii44IiBmb250LXNpemU9IjI2cHgiPjxhbmltYXRlIGFkZGl0aXZlPSJzdW0iIGF0dHJpYnV0ZU5hbWU9InN0YXJ0T2Zmc2V0IiBiZWdpbj0iMHMiIGR1cj0iNTBzIiBmcm9tPSIwJSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHRvPSIxMDAlIi8+MHgwM2E2YTg0Y2Q3NjJkOTcwN2EyMTYwNWI1NDhhYWFiODkxNTYyYWFiIOKAoiBEQUk8L3RleHRQYXRoPjx0ZXh0UGF0aCBzdGFydE9mZnNldD0iNTAlIiBocmVmPSIjRmxvYXRpbmdUZXh0IiBmaWxsPSIjZmZmIiBmb250LWZhbWlseT0iJ0NvdXJpZXIgTmV3JyxBcmlhbCxtb25vc3BhY2UiIGZpbGwtb3BhY2l0eT0iLjgiIGZvbnQtc2l6ZT0iMjZweCI+PGFuaW1hdGUgYWRkaXRpdmU9InN1bSIgYXR0cmlidXRlTmFtZT0ic3RhcnRPZmZzZXQiIGJlZ2luPSIwcyIgZHVyPSI1MHMiIGZyb209IjAlIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgdG89IjEwMCUiLz4weDAzYTZhODRjZDc2MmQ5NzA3YTIxNjA1YjU0OGFhYWI4OTE1NjJhYWIg4oCiIERBSTwvdGV4dFBhdGg+PC90ZXh0Pjx1c2UgaHJlZj0iI0dsb3ciIGZpbGwtb3BhY2l0eT0iLjkiLz48dXNlIGhyZWY9IiNHbG93IiB4PSIxMDAwIiB5PSIxMDAwIiBmaWxsLW9wYWNpdHk9Ii45Ii8+PHVzZSBocmVmPSIjTG9nbyIgeD0iMTcwIiB5PSIxNzAiIHRyYW5zZm9ybT0ic2NhbGUoLjYpIi8+PHVzZSBocmVmPSIjSG91cmdsYXNzIiB4PSIxNTAiIHk9IjkwIiB0cmFuc2Zvcm09InJvdGF0ZSgxMCkiIHRyYW5zZm9ybS1vcmlnaW49IjUwMCA1MDAiLz48dXNlIGhyZWY9IiNQcm9ncmVzcyIgeD0iMTQ0IiB5PSI3OTAiLz48dXNlIGhyZWY9IiNTdGF0dXMiIHg9IjM2OCIgeT0iNzkwIi8+PHVzZSBocmVmPSIjQW1vdW50IiB4PSI1NjgiIHk9Ijc5MCIvPjx1c2UgaHJlZj0iI0R1cmF0aW9uIiB4PSI3MDQiIHk9Ijc5MCIvPjwvc3ZnPg=="}'; - assertEq(actualDecodedTokenURI, expectedDecodedTokenURI, "decoded token URI"); - } - - function test_WhenTokenURINotDecoded() external skipOnMismatch givenNFTExists { - vm.warp({ newTimestamp: defaults.START_TIME() + defaults.TOTAL_DURATION() / 4 }); - - string memory actualTokenURI = lockup.tokenURI(defaultStreamId); - string memory expectedTokenURI = - "data:application/json;base64,eyJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoiQXNzZXQiLCJ2YWx1ZSI6IkRBSSJ9LHsidHJhaXRfdHlwZSI6IlNlbmRlciIsInZhbHVlIjoiMHg2MzMyZTdiMWRlYjFmMWEwYjc3YjJiYjE4YjE0NDMzMGM3MjkxYmNhIn0seyJ0cmFpdF90eXBlIjoiU3RhdHVzIiwidmFsdWUiOiJTdHJlYW1pbmcifV0sImRlc2NyaXB0aW9uIjoiVGhpcyBORlQgcmVwcmVzZW50cyBhIHBheW1lbnQgc3RyZWFtIGluIGEgU2FibGllciBWMiBMb2NrdXAgRHluYW1pYyBjb250cmFjdC4gVGhlIG93bmVyIG9mIHRoaXMgTkZUIGNhbiB3aXRoZHJhdyB0aGUgc3RyZWFtZWQgYXNzZXRzLCB3aGljaCBhcmUgZGVub21pbmF0ZWQgaW4gREFJLlxuXG4tIFN0cmVhbSBJRDogMVxuLSBMb2NrdXAgRHluYW1pYyBBZGRyZXNzOiAweGRiMjVhN2I3NjgzMTFkZTEyOGJiZGE3Yjg0MjZjM2Y5Yzc0ZjMyNDBcbi0gREFJIEFkZHJlc3M6IDB4MDNhNmE4NGNkNzYyZDk3MDdhMjE2MDViNTQ4YWFhYjg5MTU2MmFhYlxuXG7imqDvuI8gV0FSTklORzogVHJhbnNmZXJyaW5nIHRoZSBORlQgbWFrZXMgdGhlIG5ldyBvd25lciB0aGUgcmVjaXBpZW50IG9mIHRoZSBzdHJlYW0uIFRoZSBmdW5kcyBhcmUgbm90IGF1dG9tYXRpY2FsbHkgd2l0aGRyYXduIGZvciB0aGUgcHJldmlvdXMgcmVjaXBpZW50LiIsImV4dGVybmFsX3VybCI6Imh0dHBzOi8vc2FibGllci5jb20iLCJuYW1lIjoiU2FibGllciBWMiBMb2NrdXAgRHluYW1pYyAjMSIsImltYWdlIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0l4TURBd0lpQm9aV2xuYUhROUlqRXdNREFpSUhacFpYZENiM2c5SWpBZ01DQXhNREF3SURFd01EQWlQanh5WldOMElIZHBaSFJvUFNJeE1EQWxJaUJvWldsbmFIUTlJakV3TUNVaUlHWnBiSFJsY2owaWRYSnNLQ05PYjJselpTa2lMejQ4Y21WamRDQjRQU0kzTUNJZ2VUMGlOekFpSUhkcFpIUm9QU0k0TmpBaUlHaGxhV2RvZEQwaU9EWXdJaUJtYVd4c1BTSWpabVptSWlCbWFXeHNMVzl3WVdOcGRIazlJaTR3TXlJZ2NuZzlJalExSWlCeWVUMGlORFVpSUhOMGNtOXJaVDBpSTJabVppSWdjM1J5YjJ0bExXOXdZV05wZEhrOUlpNHhJaUJ6ZEhKdmEyVXRkMmxrZEdnOUlqUWlMejQ4WkdWbWN6NDhZMmx5WTJ4bElHbGtQU0pIYkc5M0lpQnlQU0kxTURBaUlHWnBiR3c5SW5WeWJDZ2pVbUZrYVdGc1IyeHZkeWtpTHo0OFptbHNkR1Z5SUdsa1BTSk9iMmx6WlNJK1BHWmxSbXh2YjJRZ2VEMGlNQ0lnZVQwaU1DSWdkMmxrZEdnOUlqRXdNQ1VpSUdobGFXZG9kRDBpTVRBd0pTSWdabXh2YjJRdFkyOXNiM0k5SW1oemJDZ3lNekFzTWpFbExERXhKU2tpSUdac2IyOWtMVzl3WVdOcGRIazlJakVpSUhKbGMzVnNkRDBpWm14dmIyUkdhV3hzSWk4K1BHWmxWSFZ5WW5Wc1pXNWpaU0JpWVhObFJuSmxjWFZsYm1ONVBTSXVOQ0lnYm5WdFQyTjBZWFpsY3owaU15SWdjbVZ6ZFd4MFBTSk9iMmx6WlNJZ2RIbHdaVDBpWm5KaFkzUmhiRTV2YVhObElpOCtQR1psUW14bGJtUWdhVzQ5SWs1dmFYTmxJaUJwYmpJOUltWnNiMjlrUm1sc2JDSWdiVzlrWlQwaWMyOW1kQzFzYVdkb2RDSXZQand2Wm1sc2RHVnlQanh3WVhSb0lHbGtQU0pNYjJkdklpQm1hV3hzUFNJalptWm1JaUJtYVd4c0xXOXdZV05wZEhrOUlpNHhJaUJrUFNKdE1UTXpMalUxT1N3eE1qUXVNRE0wWXkwdU1ERXpMREl1TkRFeUxURXVNRFU1TERRdU9EUTRMVEl1T1RJekxEWXVOREF5TFRJdU5UVTRMREV1T0RFNUxUVXVNVFk0TERNdU5ETTVMVGN1T0RnNExEUXVPVGsyTFRFMExqUTBMRGd1TWpZeUxUTXhMakEwTnl3eE1pNDFOalV0TkRjdU5qYzBMREV5TGpVMk9TMDRMamcxT0M0d016WXRNVGN1T0RNNExURXVNamN5TFRJMkxqTXlPQzB6TGpZMk15MDVMamd3TmkweUxqYzJOaTB4T1M0d09EY3ROeTR4TVRNdE1qY3VOVFl5TFRFeUxqYzNPQzB4TXk0NE5ESXRPQzR3TWpVc09TNDBOamd0TWpndU5qQTJMREUyTGpFMU15MHpOUzR5TmpWb01HTXlMakF6TlMweExqZ3pPQ3cwTGpJMU1pMHpMalUwTml3MkxqUTJNeTAxTGpJeU5HZ3dZell1TkRJNUxUVXVOalUxTERFMkxqSXhPQzB5TGpnek5Td3lNQzR6TlRnc05DNHhOeXcwTGpFME15dzFMakExTnl3NExqZ3hOaXc1TGpZME9Td3hNeTQ1TWl3eE15NDNNelJvTGpBek4yTTFMamN6Tml3MkxqUTJNU3d4TlM0ek5UY3RNaTR5TlRNc09TNHpPQzA0TGpRNExEQXNNQzB6TGpVeE5TMHpMalV4TlMwekxqVXhOUzB6TGpVeE5TMHhNUzQwT1MweE1TNDBOemd0TlRJdU5qVTJMVFV5TGpZMk5DMDJOQzQ0TXpjdE5qUXVPRE0zYkM0d05Ea3RMakF6TjJNdE1TNDNNalV0TVM0Mk1EWXRNaTQzTVRrdE15NDRORGN0TWk0M05URXROaTR5TURSb01HTXRMakEwTmkweUxqTTNOU3d4TGpBMk1pMDBMalU0TWl3eUxqY3lOaTAyTGpJeU9XZ3diQzR4T0RVdExqRTBPR2d3WXk0d09Ua3RMakEyTWl3dU1qSXlMUzR4TkRnc0xqTTNMUzR5TlRsb01HTXlMakEyTFRFdU16WXlMRE11T1RVeExUSXVOakl4TERZdU1EUTBMVE11T0RReVF6VTNMamMyTXkwekxqUTNNeXc1Tnk0M05pMHlMak0wTVN3eE1qZ3VOak0zTERFNExqTXpNbU14Tmk0Mk56RXNPUzQ1TkRZdE1qWXVNelEwTERVMExqZ3hNeTB6T0M0Mk5URXNOREF1TVRrNUxUWXVNams1TFRZdU1EazJMVEU0TGpBMk15MHhOeTQzTkRNdE1Ua3VOalk0TFRFNExqZ3hNUzAyTGpBeE5pMDBMakEwTnkweE15NHdOakVzTkM0M056WXROeTQzTlRJc09TNDNOVEZzTmpndU1qVTBMRFk0TGpNM01XTXhMamN5TkN3eExqWXdNU3d5TGpjeE5Dd3pMamcwTERJdU56TTRMRFl1TVRreVdpSXZQanh3WVhSb0lHbGtQU0pHYkc5aGRHbHVaMVJsZUhRaUlHWnBiR3c5SW01dmJtVWlJR1E5SWsweE1qVWdORFZvTnpVd2N6Z3dJREFnT0RBZ09EQjJOelV3Y3pBZ09EQWdMVGd3SURnd2FDMDNOVEJ6TFRnd0lEQWdMVGd3SUMwNE1IWXROelV3Y3pBZ0xUZ3dJRGd3SUMwNE1DSXZQanh5WVdScFlXeEhjbUZrYVdWdWRDQnBaRDBpVW1Ga2FXRnNSMnh2ZHlJK1BITjBiM0FnYjJabWMyVjBQU0l3SlNJZ2MzUnZjQzFqYjJ4dmNqMGlhSE5zS0RZeExEZzRKU3cwTUNVcElpQnpkRzl3TFc5d1lXTnBkSGs5SWk0MklpOCtQSE4wYjNBZ2IyWm1jMlYwUFNJeE1EQWxJaUJ6ZEc5d0xXTnZiRzl5UFNKb2Myd29Nak13TERJeEpTd3hNU1VwSWlCemRHOXdMVzl3WVdOcGRIazlJakFpTHo0OEwzSmhaR2xoYkVkeVlXUnBaVzUwUGp4c2FXNWxZWEpIY21Ga2FXVnVkQ0JwWkQwaVUyRnVaRlJ2Y0NJZ2VERTlJakFsSWlCNU1UMGlNQ1VpUGp4emRHOXdJRzltWm5ObGREMGlNQ1VpSUhOMGIzQXRZMjlzYjNJOUltaHpiQ2cyTVN3NE9DVXNOREFsS1NJdlBqeHpkRzl3SUc5bVpuTmxkRDBpTVRBd0pTSWdjM1J2Y0MxamIyeHZjajBpYUhOc0tESXpNQ3d5TVNVc01URWxLU0l2UGp3dmJHbHVaV0Z5UjNKaFpHbGxiblErUEd4cGJtVmhja2R5WVdScFpXNTBJR2xrUFNKVFlXNWtRbTkwZEc5dElpQjRNVDBpTVRBd0pTSWdlVEU5SWpFd01DVWlQanh6ZEc5d0lHOW1abk5sZEQwaU1UQWxJaUJ6ZEc5d0xXTnZiRzl5UFNKb2Myd29Nak13TERJeEpTd3hNU1VwSWk4K1BITjBiM0FnYjJabWMyVjBQU0l4TURBbElpQnpkRzl3TFdOdmJHOXlQU0pvYzJ3b05qRXNPRGdsTERRd0pTa2lMejQ4WVc1cGJXRjBaU0JoZEhSeWFXSjFkR1ZPWVcxbFBTSjRNU0lnWkhWeVBTSTJjeUlnY21Wd1pXRjBRMjkxYm5ROUltbHVaR1ZtYVc1cGRHVWlJSFpoYkhWbGN6MGlNekFsT3pZd0pUc3hNakFsT3pZd0pUc3pNQ1U3SWk4K1BDOXNhVzVsWVhKSGNtRmthV1Z1ZEQ0OGJHbHVaV0Z5UjNKaFpHbGxiblFnYVdROUlraHZkWEpuYkdGemMxTjBjbTlyWlNJZ1ozSmhaR2xsYm5SVWNtRnVjMlp2Y20wOUluSnZkR0YwWlNnNU1Da2lJR2R5WVdScFpXNTBWVzVwZEhNOUluVnpaWEpUY0dGalpVOXVWWE5sSWo0OGMzUnZjQ0J2Wm1aelpYUTlJalV3SlNJZ2MzUnZjQzFqYjJ4dmNqMGlhSE5zS0RZeExEZzRKU3cwTUNVcElpOCtQSE4wYjNBZ2IyWm1jMlYwUFNJNE1DVWlJSE4wYjNBdFkyOXNiM0k5SW1oemJDZ3lNekFzTWpFbExERXhKU2tpTHo0OEwyeHBibVZoY2tkeVlXUnBaVzUwUGp4bklHbGtQU0pJYjNWeVoyeGhjM01pUGp4d1lYUm9JR1E5SWswZ05UQXNNell3SUdFZ016QXdMRE13TUNBd0lERXNNU0EyTURBc01DQmhJRE13TUN3ek1EQWdNQ0F4TERFZ0xUWXdNQ3d3SWlCbWFXeHNQU0lqWm1abUlpQm1hV3hzTFc5d1lXTnBkSGs5SWk0d01pSWdjM1J5YjJ0bFBTSjFjbXdvSTBodmRYSm5iR0Z6YzFOMGNtOXJaU2tpSUhOMGNtOXJaUzEzYVdSMGFEMGlOQ0l2UGp4d1lYUm9JR1E5SW0wMU5qWXNNVFl4TGpJd01YWXROVE11T1RJMFl6QXRNVGt1TXpneUxUSXlMalV4TXkwek55NDFOak10TmpNdU16azRMVFV4TGpFNU9DMDBNQzQzTlRZdE1UTXVOVGt5TFRrMExqazBOaTB5TVM0d056a3RNVFV5TGpVNE55MHlNUzR3TnpsekxURXhNUzQ0TXpnc055NDBPRGN0TVRVeUxqWXdNaXd5TVM0d056bGpMVFF3TGpnNU15d3hNeTQyTXpZdE5qTXVOREV6TERNeExqZ3hOaTAyTXk0ME1UTXNOVEV1TVRrNGRqVXpMamt5TkdNd0xERTNMakU0TVN3eE55NDNNRFFzTXpNdU5ESTNMRFV3TGpJeU15dzBOaTR6T1RSMk1qZzBMamd3T1dNdE16SXVOVEU1TERFeUxqazJMVFV3TGpJeU15d3lPUzR5TURZdE5UQXVNakl6TERRMkxqTTVOSFkxTXk0NU1qUmpNQ3d4T1M0ek9ESXNNakl1TlRJc016Y3VOVFl6TERZekxqUXhNeXcxTVM0eE9UZ3NOREF1TnpZekxERXpMalU1TWl3NU5DNDVOVFFzTWpFdU1EYzVMREUxTWk0Mk1ESXNNakV1TURjNWN6RXhNUzQ0TXpFdE55NDBPRGNzTVRVeUxqVTROeTB5TVM0d056bGpOREF1T0RnMkxURXpMall6Tml3Mk15NHpPVGd0TXpFdU9ERTJMRFl6TGpNNU9DMDFNUzR4T1RoMkxUVXpMamt5TkdNd0xURTNMakU1TmkweE55NDNNRFF0TXpNdU5ETTFMVFV3TGpJeU15MDBOaTQwTURGV01qQTNMall3TTJNek1pNDFNVGt0TVRJdU9UWTNMRFV3TGpJeU15MHlPUzR5TURZc05UQXVNakl6TFRRMkxqUXdNVnB0TFRNME55NDBOaklzTlRjdU56a3piREV6TUM0NU5Ua3NNVE14TGpBeU55MHhNekF1T1RVNUxERXpNUzR3TVROV01qRTRMams1TkZwdE1qWXlMamt5TkM0d01qSjJNall5TGpBeE9Hd3RNVE13TGprek55MHhNekV1TURBMkxERXpNQzQ1TXpjdE1UTXhMakF4TTFvaUlHWnBiR3c5SWlNeE5qRTRNaklpUGp3dmNHRjBhRDQ4Y0c5c2VXZHZiaUJ3YjJsdWRITTlJak0xTUNBek5UQXVNREkySURReE5TNHdNeUF5T0RRdU9UYzRJREk0TlNBeU9EUXVPVGM0SURNMU1DQXpOVEF1TURJMklpQm1hV3hzUFNKMWNtd29JMU5oYm1SQ2IzUjBiMjBwSWk4K1BIQmhkR2dnWkQwaWJUUXhOaTR6TkRFc01qZ3hMamszTldNd0xDNDVNVFF0TGpNMU5Dd3hMamd3T1MweExqQXpOU3d5TGpZNExUVXVOVFF5TERjdU1EYzJMVE15TGpZMk1Td3hNaTQwTlMwMk5TNHlPQ3d4TWk0ME5TMHpNaTQyTWpRc01DMDFPUzQzTXpndE5TNHpOelF0TmpVdU1qZ3RNVEl1TkRVdExqWTRNUzB1T0RjeUxURXVNRE0xTFRFdU56WTNMVEV1TURNMUxUSXVOamdzTUMwdU9URTBMak0xTkMweExqZ3dPQ3d4TGpBek5TMHlMalkzTml3MUxqVTBNaTAzTGpBM05pd3pNaTQyTlRZdE1USXVORFVzTmpVdU1qZ3RNVEl1TkRVc016SXVOakU1TERBc05Ua3VOek00TERVdU16YzBMRFkxTGpJNExERXlMalExTGpZNE1TNDROamNzTVM0d016VXNNUzQzTmpJc01TNHdNelVzTWk0Mk56WmFJaUJtYVd4c1BTSjFjbXdvSTFOaGJtUlViM0FwSWk4K1BIQmhkR2dnWkQwaWJUUTRNUzQwTml3MU1EUXVNVEF4ZGpVNExqUTBPV010TWk0ek5TNDNOeTAwTGpneUxERXVOVEV0Tnk0ek9Td3lMakl6TFRNd0xqTXNPQzQxTkMwM05DNDJOU3d4TXk0NU1pMHhNalF1TURZc01UTXVPVEl0TlRNdU5pd3dMVEV3TVM0eU5DMDJMak16TFRFek1TNDBOeTB4Tmk0eE5uWXROVGd1TkRNNWFESTJNaTQ1TWxvaUlHWnBiR3c5SW5WeWJDZ2pVMkZ1WkVKdmRIUnZiU2tpTHo0OFpXeHNhWEJ6WlNCamVEMGlNelV3SWlCamVUMGlOVEEwTGpFd01TSWdjbmc5SWpFek1TNDBOaklpSUhKNVBTSXlPQzR4TURnaUlHWnBiR3c5SW5WeWJDZ2pVMkZ1WkZSdmNDa2lMejQ4WnlCbWFXeHNQU0p1YjI1bElpQnpkSEp2YTJVOUluVnliQ2dqU0c5MWNtZHNZWE56VTNSeWIydGxLU0lnYzNSeWIydGxMV3hwYm1WallYQTlJbkp2ZFc1a0lpQnpkSEp2YTJVdGJXbDBaWEpzYVcxcGREMGlNVEFpSUhOMGNtOXJaUzEzYVdSMGFEMGlOQ0krUEhCaGRHZ2daRDBpYlRVMk5TNDJOREVzTVRBM0xqSTRZekFzT1M0MU16Y3ROUzQxTml3eE9DNDJNamt0TVRVdU5qYzJMREkyTGprM00yZ3RMakF5TTJNdE9TNHlNRFFzTnk0MU9UWXRNakl1TVRrMExERTBMalUyTWkwek9DNHhPVGNzTWpBdU5Ua3lMVE01TGpVd05Dd3hOQzQ1TXpZdE9UY3VNekkxTERJMExqTTFOUzB4TmpFdU56TXpMREkwTGpNMU5TMDVNQzQwT0N3d0xURTJOeTQ1TkRndE1UZ3VOVGd5TFRFNU9TNDVOVE10TkRRdU9UUTRhQzB1TURJell5MHhNQzR4TVRVdE9DNHpORFF0TVRVdU5qYzJMVEUzTGpRek55MHhOUzQyTnpZdE1qWXVPVGN6TERBdE16a3VOek0xTERrMkxqVTFOQzAzTVM0NU1qRXNNakUxTGpZMU1pMDNNUzQ1TWpGek1qRTFMall5T1N3ek1pNHhPRFVzTWpFMUxqWXlPU3czTVM0NU1qRmFJaTgrUEhCaGRHZ2daRDBpYlRFek5DNHpOaXd4TmpFdU1qQXpZekFzTXprdU56TTFMRGsyTGpVMU5DdzNNUzQ1TWpFc01qRTFMalkxTWl3M01TNDVNakZ6TWpFMUxqWXlPUzB6TWk0eE9EWXNNakUxTGpZeU9TMDNNUzQ1TWpFaUx6NDhiR2x1WlNCNE1UMGlNVE0wTGpNMklpQjVNVDBpTVRZeExqSXdNeUlnZURJOUlqRXpOQzR6TmlJZ2VUSTlJakV3Tnk0eU9DSXZQanhzYVc1bElIZ3hQU0kxTmpVdU5qUWlJSGt4UFNJeE5qRXVNakF6SWlCNE1qMGlOVFkxTGpZMElpQjVNajBpTVRBM0xqSTRJaTgrUEd4cGJtVWdlREU5SWpFNE5DNDFPRFFpSUhreFBTSXlNRFl1T0RJeklpQjRNajBpTVRnMExqVTROU0lnZVRJOUlqVXpOeTQxTnpraUx6NDhiR2x1WlNCNE1UMGlNakU0TGpFNE1TSWdlVEU5SWpJeE9DNHhNVGdpSUhneVBTSXlNVGd1TVRneElpQjVNajBpTlRZeUxqVXpOeUl2UGp4c2FXNWxJSGd4UFNJME9ERXVPREU0SWlCNU1UMGlNakU0TGpFME1pSWdlREk5SWpRNE1TNDRNVGtpSUhreVBTSTFOakl1TkRJNElpOCtQR3hwYm1VZ2VERTlJalV4TlM0ME1UVWlJSGt4UFNJeU1EY3VNelV5SWlCNE1qMGlOVEUxTGpReE5pSWdlVEk5SWpVek55NDFOemtpTHo0OGNHRjBhQ0JrUFNKdE1UZzBMalU0TERVek55NDFPR013TERVdU5EVXNOQzR5Tnl3eE1DNDJOU3d4TWk0d015d3hOUzQwTW1ndU1ESmpOUzQxTVN3ekxqTTVMREV5TGpjNUxEWXVOVFVzTWpFdU5UVXNPUzQwTWl3ek1DNHlNU3c1TGprc056Z3VNRElzTVRZdU1qZ3NNVE14TGpnekxERTJMakk0TERRNUxqUXhMREFzT1RNdU56WXROUzR6T0N3eE1qUXVNRFl0TVRNdU9USXNNaTQzTFM0M05pdzFMakk1TFRFdU5UUXNOeTQzTlMweUxqTTFMRGd1TnpjdE1pNDROeXd4Tmk0d05TMDJMakEwTERJeExqVTJMVGt1TkROb01HTTNMamMyTFRRdU56Y3NNVEl1TURRdE9TNDVOeXd4TWk0d05DMHhOUzQwTWlJdlBqeHdZWFJvSUdROUltMHhPRFF1TlRneUxEUTVNaTQyTlRaakxUTXhMak0xTkN3eE1pNDBPRFV0TlRBdU1qSXpMREk0TGpVNExUVXdMakl5TXl3ME5pNHhORElzTUN3NUxqVXpOaXcxTGpVMk5Dd3hPQzQyTWpjc01UVXVOamMzTERJMkxqazJPV2d1TURJeVl6Z3VOVEF6TERjdU1EQTFMREl3TGpJeE15d3hNeTQwTmpNc016UXVOVEkwTERFNUxqRTFPU3c1TGprNU9Td3pMams1TVN3eU1TNHlOamtzTnk0Mk1Ea3NNek11TlRrM0xERXdMamM0T0N3ek5pNDBOU3c1TGpRd055dzRNaTR4T0RFc01UVXVNREF5TERFek1TNDRNelVzTVRVdU1EQXljemsxTGpNMk15MDFMalU1TlN3eE16RXVPREEzTFRFMUxqQXdNbU14TUM0NE5EY3RNaTQzT1N3eU1DNDROamN0TlM0NU1qWXNNamt1T1RJMExUa3VNelE1TERFdU1qUTBMUzQwTmpjc01pNDBOek10TGprME1pd3pMalkzTXkweExqUXlOQ3d4TkM0ek1qWXROUzQyT1RZc01qWXVNRE0xTFRFeUxqRTJNU3d6TkM0MU1qUXRNVGt1TVRjemFDNHdNakpqTVRBdU1URTBMVGd1TXpReUxERTFMalkzTnkweE55NDBNek1zTVRVdU5qYzNMVEkyTGprMk9Td3dMVEUzTGpVMk1pMHhPQzQ0TmprdE16TXVOalkxTFRVd0xqSXlNeTAwTmk0eE5TSXZQanh3WVhSb0lHUTlJbTB4TXpRdU16WXNOVGt5TGpjeVl6QXNNemt1TnpNMUxEazJMalUxTkN3M01TNDVNakVzTWpFMUxqWTFNaXczTVM0NU1qRnpNakUxTGpZeU9TMHpNaTR4T0RZc01qRTFMall5T1MwM01TNDVNakVpTHo0OGJHbHVaU0I0TVQwaU1UTTBMak0ySWlCNU1UMGlOVGt5TGpjeUlpQjRNajBpTVRNMExqTTJJaUI1TWowaU5UTTRMamM1TnlJdlBqeHNhVzVsSUhneFBTSTFOalV1TmpRaUlIa3hQU0kxT1RJdU56SWlJSGd5UFNJMU5qVXVOalFpSUhreVBTSTFNemd1TnprM0lpOCtQSEJ2Ykhsc2FXNWxJSEJ2YVc1MGN6MGlORGd4TGpneU1pQTBPREV1T1RBeElEUTRNUzQzT1RnZ05EZ3hMamczTnlBME9ERXVOemMxSURRNE1TNDROVFFnTXpVd0xqQXhOU0F6TlRBdU1ESTJJREl4T0M0eE9EVWdNakU0TGpFeU9TSXZQanh3YjJ4NWJHbHVaU0J3YjJsdWRITTlJakl4T0M0eE9EVWdORGd4TGprd01TQXlNVGd1TWpNeElEUTRNUzQ0TlRRZ016VXdMakF4TlNBek5UQXVNREkySURRNE1TNDRNaklnTWpFNExqRTFNaUl2UGp3dlp6NDhMMmMrUEdjZ2FXUTlJbEJ5YjJkeVpYTnpJaUJtYVd4c1BTSWpabVptSWo0OGNtVmpkQ0IzYVdSMGFEMGlNakE0SWlCb1pXbG5hSFE5SWpFd01DSWdabWxzYkMxdmNHRmphWFI1UFNJdU1ETWlJSEo0UFNJeE5TSWdjbms5SWpFMUlpQnpkSEp2YTJVOUlpTm1abVlpSUhOMGNtOXJaUzF2Y0dGamFYUjVQU0l1TVNJZ2MzUnliMnRsTFhkcFpIUm9QU0kwSWk4K1BIUmxlSFFnZUQwaU1qQWlJSGs5SWpNMElpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5eEJjbWxoYkN4dGIyNXZjM0JoWTJVaUlHWnZiblF0YzJsNlpUMGlNakp3ZUNJK1VISnZaM0psYzNNOEwzUmxlSFErUEhSbGVIUWdlRDBpTWpBaUlIazlJamN5SWlCbWIyNTBMV1poYldsc2VUMGlKME52ZFhKcFpYSWdUbVYzSnl4QmNtbGhiQ3h0YjI1dmMzQmhZMlVpSUdadmJuUXRjMmw2WlQwaU1qWndlQ0krTWpVbFBDOTBaWGgwUGp4bklHWnBiR3c5SW01dmJtVWlQanhqYVhKamJHVWdZM2c5SWpFMk5pSWdZM2s5SWpVd0lpQnlQU0l5TWlJZ2MzUnliMnRsUFNKb2Myd29Nak13TERJeEpTd3hNU1VwSWlCemRISnZhMlV0ZDJsa2RHZzlJakV3SWk4K1BHTnBjbU5zWlNCamVEMGlNVFkySWlCamVUMGlOVEFpSUhCaGRHaE1aVzVuZEdnOUlqRXdNREF3SWlCeVBTSXlNaUlnYzNSeWIydGxQU0pvYzJ3b05qRXNPRGdsTERRd0pTa2lJSE4wY205clpTMWtZWE5vWVhKeVlYazlJakV3TURBd0lpQnpkSEp2YTJVdFpHRnphRzltWm5ObGREMGlOelV3TUNJZ2MzUnliMnRsTFd4cGJtVmpZWEE5SW5KdmRXNWtJaUJ6ZEhKdmEyVXRkMmxrZEdnOUlqVWlJSFJ5WVc1elptOXliVDBpY205MFlYUmxLQzA1TUNraUlIUnlZVzV6Wm05eWJTMXZjbWxuYVc0OUlqRTJOaUExTUNJdlBqd3ZaejQ4TDJjK1BHY2dhV1E5SWxOMFlYUjFjeUlnWm1sc2JEMGlJMlptWmlJK1BISmxZM1FnZDJsa2RHZzlJakU0TkNJZ2FHVnBaMmgwUFNJeE1EQWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqQXpJaUJ5ZUQwaU1UVWlJSEo1UFNJeE5TSWdjM1J5YjJ0bFBTSWpabVptSWlCemRISnZhMlV0YjNCaFkybDBlVDBpTGpFaUlITjBjbTlyWlMxM2FXUjBhRDBpTkNJdlBqeDBaWGgwSUhnOUlqSXdJaUI1UFNJek5DSWdabTl1ZEMxbVlXMXBiSGs5SWlkRGIzVnlhV1Z5SUU1bGR5Y3NRWEpwWVd3c2JXOXViM053WVdObElpQm1iMjUwTFhOcGVtVTlJakl5Y0hnaVBsTjBZWFIxY3p3dmRHVjRkRDQ4ZEdWNGRDQjRQU0l5TUNJZ2VUMGlOeklpSUdadmJuUXRabUZ0YVd4NVBTSW5RMjkxY21sbGNpQk9aWGNuTEVGeWFXRnNMRzF2Ym05emNHRmpaU0lnWm05dWRDMXphWHBsUFNJeU5uQjRJajVUZEhKbFlXMXBibWM4TDNSbGVIUStQQzluUGp4bklHbGtQU0pCYlc5MWJuUWlJR1pwYkd3OUlpTm1abVlpUGp4eVpXTjBJSGRwWkhSb1BTSXhNakFpSUdobGFXZG9kRDBpTVRBd0lpQm1hV3hzTFc5d1lXTnBkSGs5SWk0d015SWdjbmc5SWpFMUlpQnllVDBpTVRVaUlITjBjbTlyWlQwaUkyWm1aaUlnYzNSeWIydGxMVzl3WVdOcGRIazlJaTR4SWlCemRISnZhMlV0ZDJsa2RHZzlJalFpTHo0OGRHVjRkQ0I0UFNJeU1DSWdlVDBpTXpRaUlHWnZiblF0Wm1GdGFXeDVQU0luUTI5MWNtbGxjaUJPWlhjbkxFRnlhV0ZzTEcxdmJtOXpjR0ZqWlNJZ1ptOXVkQzF6YVhwbFBTSXlNbkI0SWo1QmJXOTFiblE4TDNSbGVIUStQSFJsZUhRZ2VEMGlNakFpSUhrOUlqY3lJaUJtYjI1MExXWmhiV2xzZVQwaUowTnZkWEpwWlhJZ1RtVjNKeXhCY21saGJDeHRiMjV2YzNCaFkyVWlJR1p2Ym5RdGMybDZaVDBpTWpad2VDSStKaU00T0RBMU95QXhNRXM4TDNSbGVIUStQQzluUGp4bklHbGtQU0pFZFhKaGRHbHZiaUlnWm1sc2JEMGlJMlptWmlJK1BISmxZM1FnZDJsa2RHZzlJakUxTWlJZ2FHVnBaMmgwUFNJeE1EQWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqQXpJaUJ5ZUQwaU1UVWlJSEo1UFNJeE5TSWdjM1J5YjJ0bFBTSWpabVptSWlCemRISnZhMlV0YjNCaFkybDBlVDBpTGpFaUlITjBjbTlyWlMxM2FXUjBhRDBpTkNJdlBqeDBaWGgwSUhnOUlqSXdJaUI1UFNJek5DSWdabTl1ZEMxbVlXMXBiSGs5SWlkRGIzVnlhV1Z5SUU1bGR5Y3NRWEpwWVd3c2JXOXViM053WVdObElpQm1iMjUwTFhOcGVtVTlJakl5Y0hnaVBrUjFjbUYwYVc5dVBDOTBaWGgwUGp4MFpYaDBJSGc5SWpJd0lpQjVQU0kzTWlJZ1ptOXVkQzFtWVcxcGJIazlJaWREYjNWeWFXVnlJRTVsZHljc1FYSnBZV3dzYlc5dWIzTndZV05sSWlCbWIyNTBMWE5wZW1VOUlqSTJjSGdpUGlac2REc2dNU0JFWVhrOEwzUmxlSFErUEM5blBqd3ZaR1ZtY3o0OGRHVjRkQ0IwWlhoMExYSmxibVJsY21sdVp6MGliM0IwYVcxcGVtVlRjR1ZsWkNJK1BIUmxlSFJRWVhSb0lITjBZWEowVDJabWMyVjBQU0l0TVRBd0pTSWdhSEpsWmowaUkwWnNiMkYwYVc1blZHVjRkQ0lnWm1sc2JEMGlJMlptWmlJZ1ptOXVkQzFtWVcxcGJIazlJaWREYjNWeWFXVnlJRTVsZHljc1FYSnBZV3dzYlc5dWIzTndZV05sSWlCbWFXeHNMVzl3WVdOcGRIazlJaTQ0SWlCbWIyNTBMWE5wZW1VOUlqSTJjSGdpUGp4aGJtbHRZWFJsSUdGa1pHbDBhWFpsUFNKemRXMGlJR0YwZEhKcFluVjBaVTVoYldVOUluTjBZWEowVDJabWMyVjBJaUJpWldkcGJqMGlNSE1pSUdSMWNqMGlOVEJ6SWlCbWNtOXRQU0l3SlNJZ2NtVndaV0YwUTI5MWJuUTlJbWx1WkdWbWFXNXBkR1VpSUhSdlBTSXhNREFsSWk4K01IaGtZakkxWVRkaU56WTRNekV4WkdVeE1qaGlZbVJoTjJJNE5ESTJZek5tT1dNM05HWXpNalF3SU9LQW9pQlRZV0pzYVdWeUlGWXlJRXh2WTJ0MWNDQkVlVzVoYldsalBDOTBaWGgwVUdGMGFENDhkR1Y0ZEZCaGRHZ2djM1JoY25SUFptWnpaWFE5SWpBbElpQm9jbVZtUFNJalJteHZZWFJwYm1kVVpYaDBJaUJtYVd4c1BTSWpabVptSWlCbWIyNTBMV1poYldsc2VUMGlKME52ZFhKcFpYSWdUbVYzSnl4QmNtbGhiQ3h0YjI1dmMzQmhZMlVpSUdacGJHd3RiM0JoWTJsMGVUMGlMamdpSUdadmJuUXRjMmw2WlQwaU1qWndlQ0krUEdGdWFXMWhkR1VnWVdSa2FYUnBkbVU5SW5OMWJTSWdZWFIwY21saWRYUmxUbUZ0WlQwaWMzUmhjblJQWm1aelpYUWlJR0psWjJsdVBTSXdjeUlnWkhWeVBTSTFNSE1pSUdaeWIyMDlJakFsSWlCeVpYQmxZWFJEYjNWdWREMGlhVzVrWldacGJtbDBaU0lnZEc4OUlqRXdNQ1VpTHo0d2VHUmlNalZoTjJJM05qZ3pNVEZrWlRFeU9HSmlaR0UzWWpnME1qWmpNMlk1WXpjMFpqTXlOREFnNG9DaUlGTmhZbXhwWlhJZ1ZqSWdURzlqYTNWd0lFUjVibUZ0YVdNOEwzUmxlSFJRWVhSb1BqeDBaWGgwVUdGMGFDQnpkR0Z5ZEU5bVpuTmxkRDBpTFRVd0pTSWdhSEpsWmowaUkwWnNiMkYwYVc1blZHVjRkQ0lnWm1sc2JEMGlJMlptWmlJZ1ptOXVkQzFtWVcxcGJIazlJaWREYjNWeWFXVnlJRTVsZHljc1FYSnBZV3dzYlc5dWIzTndZV05sSWlCbWFXeHNMVzl3WVdOcGRIazlJaTQ0SWlCbWIyNTBMWE5wZW1VOUlqSTJjSGdpUGp4aGJtbHRZWFJsSUdGa1pHbDBhWFpsUFNKemRXMGlJR0YwZEhKcFluVjBaVTVoYldVOUluTjBZWEowVDJabWMyVjBJaUJpWldkcGJqMGlNSE1pSUdSMWNqMGlOVEJ6SWlCbWNtOXRQU0l3SlNJZ2NtVndaV0YwUTI5MWJuUTlJbWx1WkdWbWFXNXBkR1VpSUhSdlBTSXhNREFsSWk4K01IZ3dNMkUyWVRnMFkyUTNOakprT1Rjd04yRXlNVFl3TldJMU5EaGhZV0ZpT0RreE5UWXlZV0ZpSU9LQW9pQkVRVWs4TDNSbGVIUlFZWFJvUGp4MFpYaDBVR0YwYUNCemRHRnlkRTltWm5ObGREMGlOVEFsSWlCb2NtVm1QU0lqUm14dllYUnBibWRVWlhoMElpQm1hV3hzUFNJalptWm1JaUJtYjI1MExXWmhiV2xzZVQwaUowTnZkWEpwWlhJZ1RtVjNKeXhCY21saGJDeHRiMjV2YzNCaFkyVWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqZ2lJR1p2Ym5RdGMybDZaVDBpTWpad2VDSStQR0Z1YVcxaGRHVWdZV1JrYVhScGRtVTlJbk4xYlNJZ1lYUjBjbWxpZFhSbFRtRnRaVDBpYzNSaGNuUlBabVp6WlhRaUlHSmxaMmx1UFNJd2N5SWdaSFZ5UFNJMU1ITWlJR1p5YjIwOUlqQWxJaUJ5WlhCbFlYUkRiM1Z1ZEQwaWFXNWtaV1pwYm1sMFpTSWdkRzg5SWpFd01DVWlMejR3ZURBellUWmhPRFJqWkRjMk1tUTVOekEzWVRJeE5qQTFZalUwT0dGaFlXSTRPVEUxTmpKaFlXSWc0b0NpSUVSQlNUd3ZkR1Y0ZEZCaGRHZytQQzkwWlhoMFBqeDFjMlVnYUhKbFpqMGlJMGRzYjNjaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpraUx6NDhkWE5sSUdoeVpXWTlJaU5IYkc5M0lpQjRQU0l4TURBd0lpQjVQU0l4TURBd0lpQm1hV3hzTFc5d1lXTnBkSGs5SWk0NUlpOCtQSFZ6WlNCb2NtVm1QU0lqVEc5bmJ5SWdlRDBpTVRjd0lpQjVQU0l4TnpBaUlIUnlZVzV6Wm05eWJUMGljMk5oYkdVb0xqWXBJaTgrUEhWelpTQm9jbVZtUFNJalNHOTFjbWRzWVhOeklpQjRQU0l4TlRBaUlIazlJamt3SWlCMGNtRnVjMlp2Y20wOUluSnZkR0YwWlNneE1Da2lJSFJ5WVc1elptOXliUzF2Y21sbmFXNDlJalV3TUNBMU1EQWlMejQ4ZFhObElHaHlaV1k5SWlOUWNtOW5jbVZ6Y3lJZ2VEMGlNVFEwSWlCNVBTSTNPVEFpTHo0OGRYTmxJR2h5WldZOUlpTlRkR0YwZFhNaUlIZzlJak0yT0NJZ2VUMGlOemt3SWk4K1BIVnpaU0JvY21WbVBTSWpRVzF2ZFc1MElpQjRQU0kxTmpnaUlIazlJamM1TUNJdlBqeDFjMlVnYUhKbFpqMGlJMFIxY21GMGFXOXVJaUI0UFNJM01EUWlJSGs5SWpjNU1DSXZQand2YzNablBnPT0ifQ=="; - assertEq(actualTokenURI, expectedTokenURI, "token URI"); - } -} diff --git a/tests/integration/concrete/lockup-tranched/token-uri/tokenURI.tree b/tests/integration/concrete/lockup-tranched/token-uri/tokenURI.tree deleted file mode 100644 index 7fbcf727e..000000000 --- a/tests/integration/concrete/lockup-tranched/token-uri/tokenURI.tree +++ /dev/null @@ -1,8 +0,0 @@ -TokenURI_Lockup_Tranched_Integration_Concrete_Test -├── given NFT not exist -│ └── it should revert -└── given NFT exists - ├── when token URI decoded - │ └── it should return the correct token URI - └── when token URI not decoded - └── it should return the correct 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"); } }