From 85874e336eedc8b79e10d3f9626f3df2f18a10c2 Mon Sep 17 00:00:00 2001 From: Yash <72552910+kumaryash90@users.noreply.github.com> Date: Tue, 21 Nov 2023 03:00:12 +0530 Subject: [PATCH] BTT Split (#559) * test initialize * test setContractURI * test other functions * test release * test distribute * test distribute --- .../distribute-erc20/distribute.t.sol | 78 +++++++++++ .../distribute-erc20/distribute.tree | 6 + .../distribute-native-token/distribute.t.sol | 77 ++++++++++ .../distribute-native-token/distribute.tree | 6 + .../split-BTT/initialize/initialize.t.sol | 131 ++++++++++++++++++ src/test/split-BTT/initialize/initialize.tree | 25 ++++ .../split-BTT/other-functions/other.t.sol | 58 ++++++++ src/test/split-BTT/other-functions/other.tree | 5 + .../split-BTT/release-erc20/release.t.sol | 108 +++++++++++++++ src/test/split-BTT/release-erc20/release.tree | 12 ++ .../release-native-token/release.t.sol | 104 ++++++++++++++ .../release-native-token/release.tree | 12 ++ .../set-contract-uri/setContractURI.t.sol | 85 ++++++++++++ .../set-contract-uri/setContractURI.tree | 8 ++ 14 files changed, 715 insertions(+) create mode 100644 src/test/split-BTT/distribute-erc20/distribute.t.sol create mode 100644 src/test/split-BTT/distribute-erc20/distribute.tree create mode 100644 src/test/split-BTT/distribute-native-token/distribute.t.sol create mode 100644 src/test/split-BTT/distribute-native-token/distribute.tree create mode 100644 src/test/split-BTT/initialize/initialize.t.sol create mode 100644 src/test/split-BTT/initialize/initialize.tree create mode 100644 src/test/split-BTT/other-functions/other.t.sol create mode 100644 src/test/split-BTT/other-functions/other.tree create mode 100644 src/test/split-BTT/release-erc20/release.t.sol create mode 100644 src/test/split-BTT/release-erc20/release.tree create mode 100644 src/test/split-BTT/release-native-token/release.t.sol create mode 100644 src/test/split-BTT/release-native-token/release.tree create mode 100644 src/test/split-BTT/set-contract-uri/setContractURI.t.sol create mode 100644 src/test/split-BTT/set-contract-uri/setContractURI.tree diff --git a/src/test/split-BTT/distribute-erc20/distribute.t.sol b/src/test/split-BTT/distribute-erc20/distribute.t.sol new file mode 100644 index 000000000..4c2d9c139 --- /dev/null +++ b/src/test/split-BTT/distribute-erc20/distribute.t.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.11; + +import "../../utils/BaseTest.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; + +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +contract MySplit is Split {} + +contract SplitTest_DistributeERC20 is BaseTest { + address payable public implementation; + address payable public proxy; + + address[] public payees; + uint256[] public shares; + + address internal caller; + string internal _contractURI; + + MySplit internal splitContract; + + event PaymentReleased(address to, uint256 amount); + + function setUp() public override { + super.setUp(); + + // Deploy implementation. + implementation = payable(address(new MySplit())); + + // create 5 payees and shares + for (uint160 i = 0; i < 5; i++) { + payees.push(getActor(i + 100)); + shares.push(i + 100); + } + + caller = getActor(1); + + // Deploy proxy pointing to implementaion. + vm.prank(deployer); + proxy = payable( + address( + new TWProxy( + implementation, + abi.encodeCall(Split.initialize, (deployer, CONTRACT_URI, forwarders(), payees, shares)) + ) + ) + ); + + splitContract = MySplit(proxy); + _contractURI = "ipfs://contracturi"; + + erc20.mint(address(splitContract), 100 ether); + } + + function test_distribute() public { + uint256[] memory pendingAmounts = new uint256[](payees.length); + + // get pending payments + for (uint256 i = 0; i < 5; i++) { + pendingAmounts[i] = splitContract.releasable(IERC20Upgradeable(address(erc20)), payees[i]); + } + + // distribute + splitContract.distribute(IERC20Upgradeable(address(erc20))); + + uint256 totalPaid; + for (uint256 i = 0; i < 5; i++) { + totalPaid += pendingAmounts[i]; + + assertEq(splitContract.released(IERC20Upgradeable(address(erc20)), payees[i]), pendingAmounts[i]); + assertEq(erc20.balanceOf(payees[i]), pendingAmounts[i]); + } + assertEq(splitContract.totalReleased(IERC20Upgradeable(address(erc20))), totalPaid); + + assertEq(erc20.balanceOf(address(splitContract)), 100 ether - totalPaid); + } +} diff --git a/src/test/split-BTT/distribute-erc20/distribute.tree b/src/test/split-BTT/distribute-erc20/distribute.tree new file mode 100644 index 000000000..320921cca --- /dev/null +++ b/src/test/split-BTT/distribute-erc20/distribute.tree @@ -0,0 +1,6 @@ +distribute() +├── it should update released mapping for all payees account by respective pending payments ✅ +├── it should update total released by total pending payments ✅ +├── it should send correct pending payment amounts of erc20 tokens to each account ✅ +├── it should reduce balance of contract by total paid in this call ✅ + \ No newline at end of file diff --git a/src/test/split-BTT/distribute-native-token/distribute.t.sol b/src/test/split-BTT/distribute-native-token/distribute.t.sol new file mode 100644 index 000000000..fd524c40d --- /dev/null +++ b/src/test/split-BTT/distribute-native-token/distribute.t.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.11; + +import "../../utils/BaseTest.sol"; + +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +contract MySplit is Split {} + +contract SplitTest_DistributeNativeToken is BaseTest { + address payable public implementation; + address payable public proxy; + + address[] public payees; + uint256[] public shares; + + address internal caller; + string internal _contractURI; + + MySplit internal splitContract; + + event PaymentReleased(address to, uint256 amount); + + function setUp() public override { + super.setUp(); + + // Deploy implementation. + implementation = payable(address(new MySplit())); + + // create 5 payees and shares + for (uint160 i = 0; i < 5; i++) { + payees.push(getActor(i + 100)); + shares.push(i + 100); + } + + caller = getActor(1); + + // Deploy proxy pointing to implementaion. + vm.prank(deployer); + proxy = payable( + address( + new TWProxy( + implementation, + abi.encodeCall(Split.initialize, (deployer, CONTRACT_URI, forwarders(), payees, shares)) + ) + ) + ); + + splitContract = MySplit(proxy); + _contractURI = "ipfs://contracturi"; + + vm.deal(address(splitContract), 100 ether); + } + + function test_distribute() public { + uint256[] memory pendingAmounts = new uint256[](payees.length); + + // get pending payments + for (uint256 i = 0; i < 5; i++) { + pendingAmounts[i] = splitContract.releasable(payees[i]); + } + + // distribute + splitContract.distribute(); + + uint256 totalPaid; + for (uint256 i = 0; i < 5; i++) { + totalPaid += pendingAmounts[i]; + + assertEq(splitContract.released(payees[i]), pendingAmounts[i]); + assertEq(payees[i].balance, pendingAmounts[i]); + } + assertEq(splitContract.totalReleased(), totalPaid); + + assertEq(address(splitContract).balance, 100 ether - totalPaid); + } +} diff --git a/src/test/split-BTT/distribute-native-token/distribute.tree b/src/test/split-BTT/distribute-native-token/distribute.tree new file mode 100644 index 000000000..d75f4cc53 --- /dev/null +++ b/src/test/split-BTT/distribute-native-token/distribute.tree @@ -0,0 +1,6 @@ +distribute() +├── it should update released mapping for all payees account by respective pending payments ✅ +├── it should update total released by total pending payments ✅ +├── it should send correct pending payment amounts of native tokens to each account ✅ +├── it should reduce balance of contract by total paid in this call ✅ + \ No newline at end of file diff --git a/src/test/split-BTT/initialize/initialize.t.sol b/src/test/split-BTT/initialize/initialize.t.sol new file mode 100644 index 000000000..bc75ae266 --- /dev/null +++ b/src/test/split-BTT/initialize/initialize.t.sol @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import "../../utils/BaseTest.sol"; + +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +contract MySplit is Split {} + +contract SplitTest_Initialize is BaseTest { + address payable public implementation; + address payable public proxy; + + address[] public payees; + uint256[] public shares; + + event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); + + function setUp() public override { + super.setUp(); + + // create 5 payees and shares + for (uint160 i = 0; i < 5; i++) { + payees.push(getActor(i + 100)); + shares.push(i + 100); + } + + // Deploy implementation. + implementation = payable(address(new MySplit())); + + // Deploy proxy pointing to implementaion. + vm.prank(deployer); + proxy = payable( + address( + new TWProxy( + implementation, + abi.encodeCall(Split.initialize, (deployer, CONTRACT_URI, forwarders(), payees, shares)) + ) + ) + ); + } + + function test_initialize_initializingImplementation() public { + vm.expectRevert("Initializable: contract is already initialized"); + Split(implementation).initialize(deployer, CONTRACT_URI, forwarders(), payees, shares); + } + + modifier whenNotImplementation() { + _; + } + + function test_initialize_proxyAlreadyInitialized() public whenNotImplementation { + vm.expectRevert("Initializable: contract is already initialized"); + MySplit(proxy).initialize(deployer, CONTRACT_URI, forwarders(), payees, shares); + } + + modifier whenProxyNotInitialized() { + proxy = payable(address(new TWProxy(implementation, ""))); + _; + } + + function test_initialize_payeeLengthZero() public whenNotImplementation whenProxyNotInitialized { + address[] memory _payees; + uint256[] memory _shares; + vm.expectRevert("PaymentSplitter: no payees"); + MySplit(proxy).initialize(deployer, CONTRACT_URI, forwarders(), _payees, _shares); + } + + modifier whenPayeeLengthNotZero() { + _; + } + + function test_initialize_payeesSharesUnequalLength() + public + whenNotImplementation + whenProxyNotInitialized + whenPayeeLengthNotZero + { + uint256[] memory _shares; + vm.expectRevert("PaymentSplitter: payees and shares length mismatch"); + MySplit(proxy).initialize(deployer, CONTRACT_URI, forwarders(), payees, _shares); + } + + modifier whenEqualLengths() { + _; + } + + function test_initialize() + public + whenNotImplementation + whenProxyNotInitialized + whenPayeeLengthNotZero + whenEqualLengths + { + MySplit(proxy).initialize(deployer, CONTRACT_URI, forwarders(), payees, shares); + + // check state + MySplit splitContract = MySplit(proxy); + + address[] memory _trustedForwarders = forwarders(); + for (uint256 i = 0; i < _trustedForwarders.length; i++) { + assertTrue(splitContract.isTrustedForwarder(_trustedForwarders[i])); + } + + uint256 totalShares; + for (uint160 i = 0; i < 5; i++) { + uint256 _shares = splitContract.shares(payees[i]); + assertEq(_shares, shares[i]); + + totalShares += _shares; + } + assertEq(totalShares, splitContract.totalShares()); + assertEq(splitContract.payeeCount(), payees.length); + assertEq(splitContract.contractURI(), CONTRACT_URI); + assertTrue(splitContract.hasRole(bytes32(0x00), deployer)); + } + + function test_initialize_event_RoleGranted_DefaultAdmin() + public + whenNotImplementation + whenProxyNotInitialized + whenPayeeLengthNotZero + whenEqualLengths + { + bytes32 _defaultAdminRole = bytes32(0x00); + vm.prank(deployer); + vm.expectEmit(true, true, true, false); + emit RoleGranted(_defaultAdminRole, deployer, deployer); + MySplit(proxy).initialize(deployer, CONTRACT_URI, forwarders(), payees, shares); + } +} diff --git a/src/test/split-BTT/initialize/initialize.tree b/src/test/split-BTT/initialize/initialize.tree new file mode 100644 index 000000000..4647d7f70 --- /dev/null +++ b/src/test/split-BTT/initialize/initialize.tree @@ -0,0 +1,25 @@ +initialize( + address _defaultAdmin, + string memory _contractURI, + address[] memory _trustedForwarders, + address[] memory _payees, + uint256[] memory _shares +) +├── when initializing the implementation contract (not proxy) +│ └── it should revert ✅ +└── when it is a proxy to the implementation + └── when it is already initialized + │ └── it should revert ✅ + └── when it is not initialized + └── when `_payees` length is zero + │ └── it should revert ✅ + └── `_payees` length is not zero + └── when `_payees` length not equal to `_shares` length + │ └── it should revert ✅ + └── when `_payees` length equal to `_shares` length + └── it should set trustedForwarder mapping to true for all addresses in `_trustedForwarders` ✅ + └── it should correctly save `_payees` and `_shares` in state ✅ + └── it should set contractURI to `_contractURI` param value ✅ + └── it should grant 0x00 (DEFAULT_ADMIN_ROLE) to `_defaultAdmin` address ✅ + └── it should emit RoleGranted event ✅ + diff --git a/src/test/split-BTT/other-functions/other.t.sol b/src/test/split-BTT/other-functions/other.t.sol new file mode 100644 index 000000000..2bf87b871 --- /dev/null +++ b/src/test/split-BTT/other-functions/other.t.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import "../../utils/BaseTest.sol"; + +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +contract MySplit is Split {} + +contract SplitTest_OtherFunctions is BaseTest { + address payable public implementation; + address payable public proxy; + + address[] public payees; + uint256[] public shares; + + address internal caller; + string internal _contractURI; + + MySplit internal splitContract; + + function setUp() public override { + super.setUp(); + + // Deploy implementation. + implementation = payable(address(new MySplit())); + + // create 5 payees and shares + for (uint160 i = 0; i < 5; i++) { + payees.push(getActor(i + 100)); + shares.push(i + 100); + } + + caller = getActor(1); + + // Deploy proxy pointing to implementaion. + vm.prank(deployer); + proxy = payable( + address( + new TWProxy( + implementation, + abi.encodeCall(Split.initialize, (deployer, CONTRACT_URI, forwarders(), payees, shares)) + ) + ) + ); + + splitContract = MySplit(proxy); + _contractURI = "ipfs://contracturi"; + } + + function test_contractType() public { + assertEq(splitContract.contractType(), bytes32("Split")); + } + + function test_contractVersion() public { + assertEq(splitContract.contractVersion(), uint8(1)); + } +} diff --git a/src/test/split-BTT/other-functions/other.tree b/src/test/split-BTT/other-functions/other.tree new file mode 100644 index 000000000..dd1798caa --- /dev/null +++ b/src/test/split-BTT/other-functions/other.tree @@ -0,0 +1,5 @@ +contractType() +├── it should return bytes32("TokenERC721") ✅ + +contractVersion() +├── it should return uint8(1) ✅ diff --git a/src/test/split-BTT/release-erc20/release.t.sol b/src/test/split-BTT/release-erc20/release.t.sol new file mode 100644 index 000000000..c5499f664 --- /dev/null +++ b/src/test/split-BTT/release-erc20/release.t.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.11; + +import "../../utils/BaseTest.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; + +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +contract MySplit is Split {} + +contract SplitTest_ReleaseERC20 is BaseTest { + address payable public implementation; + address payable public proxy; + + address[] public payees; + uint256[] public shares; + + address internal caller; + string internal _contractURI; + + MySplit internal splitContract; + + event ERC20PaymentReleased(IERC20Upgradeable indexed token, address to, uint256 amount); + + function setUp() public override { + super.setUp(); + + // Deploy implementation. + implementation = payable(address(new MySplit())); + + // create 5 payees and shares + for (uint160 i = 0; i < 5; i++) { + payees.push(getActor(i + 100)); + shares.push(i + 100); + } + + caller = getActor(1); + + // Deploy proxy pointing to implementaion. + vm.prank(deployer); + proxy = payable( + address( + new TWProxy( + implementation, + abi.encodeCall(Split.initialize, (deployer, CONTRACT_URI, forwarders(), payees, shares)) + ) + ) + ); + + splitContract = MySplit(proxy); + _contractURI = "ipfs://contracturi"; + } + + function test_release_zeroShares() public { + vm.expectRevert("PaymentSplitter: account has no shares"); + splitContract.release(IERC20Upgradeable(address(erc20)), payable(address(0x123))); // arbitrary address + } + + modifier whenNonZeroShares() { + _; + } + + function test_release_pendingPaymentZero() public { + vm.expectRevert("PaymentSplitter: account is not due payment"); + splitContract.release(IERC20Upgradeable(address(erc20)), payable(payees[1])); + } + + modifier whenPendingPaymentNonZero() { + erc20.mint(address(splitContract), 100 ether); + _; + } + + function test_release() public whenPendingPaymentNonZero { + address _payeeOne = payees[1]; // select a payee from the array + uint256 pendingPayment = splitContract.releasable(IERC20Upgradeable(address(erc20)), _payeeOne); + + splitContract.release(IERC20Upgradeable(address(erc20)), payable(_payeeOne)); + + uint256 totalReleased = splitContract.totalReleased(IERC20Upgradeable(address(erc20))); + assertEq(splitContract.released(IERC20Upgradeable(address(erc20)), _payeeOne), pendingPayment); + assertEq(totalReleased, pendingPayment); + assertEq(erc20.balanceOf(_payeeOne), pendingPayment); + + // check for another payee + address _payeeThree = payees[3]; + pendingPayment = splitContract.releasable(IERC20Upgradeable(address(erc20)), _payeeThree); + + splitContract.release(IERC20Upgradeable(address(erc20)), payable(_payeeThree)); + + assertEq(splitContract.released(IERC20Upgradeable(address(erc20)), _payeeThree), pendingPayment); + assertEq(splitContract.totalReleased(IERC20Upgradeable(address(erc20))), totalReleased + pendingPayment); + assertEq(erc20.balanceOf(_payeeThree), pendingPayment); + + assertEq( + erc20.balanceOf(address(splitContract)), + 100 ether - erc20.balanceOf(_payeeOne) - erc20.balanceOf(_payeeThree) + ); + } + + function test_release_event_PaymentReleased() public whenPendingPaymentNonZero { + address _payeeOne = payees[1]; // select a payee from the array + uint256 pendingPayment = splitContract.releasable(IERC20Upgradeable(address(erc20)), _payeeOne); + + vm.expectEmit(true, false, false, true); + emit ERC20PaymentReleased(IERC20Upgradeable(address(erc20)), _payeeOne, pendingPayment); + splitContract.release(IERC20Upgradeable(address(erc20)), payable(_payeeOne)); + } +} diff --git a/src/test/split-BTT/release-erc20/release.tree b/src/test/split-BTT/release-erc20/release.tree new file mode 100644 index 000000000..217ea3b6c --- /dev/null +++ b/src/test/split-BTT/release-erc20/release.tree @@ -0,0 +1,12 @@ +release(address payable account) +├── when account has zero shares + │ └── it should revert ✅ + └── when account has non-zero shares + └── when pending payment is zero + │ └── it should revert ✅ + └── when pending payment is not zero + └── it should update released mapping for the account by pending payment ✅ + └── it should update total released by pending payment ✅ + └── it should send pending payment amount of erc20 token to account ✅ + └── it should emit ERC20PaymentReleased event ✅ + \ No newline at end of file diff --git a/src/test/split-BTT/release-native-token/release.t.sol b/src/test/split-BTT/release-native-token/release.t.sol new file mode 100644 index 000000000..18210e0da --- /dev/null +++ b/src/test/split-BTT/release-native-token/release.t.sol @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.11; + +import "../../utils/BaseTest.sol"; + +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +contract MySplit is Split {} + +contract SplitTest_ReleaseNativeToken is BaseTest { + address payable public implementation; + address payable public proxy; + + address[] public payees; + uint256[] public shares; + + address internal caller; + string internal _contractURI; + + MySplit internal splitContract; + + event PaymentReleased(address to, uint256 amount); + + function setUp() public override { + super.setUp(); + + // Deploy implementation. + implementation = payable(address(new MySplit())); + + // create 5 payees and shares + for (uint160 i = 0; i < 5; i++) { + payees.push(getActor(i + 100)); + shares.push(i + 100); + } + + caller = getActor(1); + + // Deploy proxy pointing to implementaion. + vm.prank(deployer); + proxy = payable( + address( + new TWProxy( + implementation, + abi.encodeCall(Split.initialize, (deployer, CONTRACT_URI, forwarders(), payees, shares)) + ) + ) + ); + + splitContract = MySplit(proxy); + _contractURI = "ipfs://contracturi"; + } + + function test_release_zeroShares() public { + vm.expectRevert("PaymentSplitter: account has no shares"); + splitContract.release(payable(address(0x123))); // arbitrary address + } + + modifier whenNonZeroShares() { + _; + } + + function test_release_pendingPaymentZero() public { + vm.expectRevert("PaymentSplitter: account is not due payment"); + splitContract.release(payable(payees[1])); + } + + modifier whenPendingPaymentNonZero() { + vm.deal(address(splitContract), 100 ether); + _; + } + + function test_release() public whenPendingPaymentNonZero { + address _payeeOne = payees[1]; // select a payee from the array + uint256 pendingPayment = splitContract.releasable(_payeeOne); + + splitContract.release(payable(_payeeOne)); + + uint256 totalReleased = splitContract.totalReleased(); + assertEq(splitContract.released(_payeeOne), pendingPayment); + assertEq(totalReleased, pendingPayment); + assertEq(_payeeOne.balance, pendingPayment); + + // check for another payee + address _payeeThree = payees[3]; + pendingPayment = splitContract.releasable(_payeeThree); + + splitContract.release(payable(_payeeThree)); + + assertEq(splitContract.released(_payeeThree), pendingPayment); + assertEq(splitContract.totalReleased(), totalReleased + pendingPayment); + assertEq(_payeeThree.balance, pendingPayment); + + assertEq(address(splitContract).balance, 100 ether - _payeeOne.balance - _payeeThree.balance); + } + + function test_release_event_PaymentReleased() public whenPendingPaymentNonZero { + address _payeeOne = payees[1]; // select a payee from the array + uint256 pendingPayment = splitContract.releasable(_payeeOne); + + vm.expectEmit(false, false, false, true); + emit PaymentReleased(_payeeOne, pendingPayment); + splitContract.release(payable(_payeeOne)); + } +} diff --git a/src/test/split-BTT/release-native-token/release.tree b/src/test/split-BTT/release-native-token/release.tree new file mode 100644 index 000000000..afa44e86d --- /dev/null +++ b/src/test/split-BTT/release-native-token/release.tree @@ -0,0 +1,12 @@ +release(address payable account) +├── when account has zero shares + │ └── it should revert ✅ + └── when account has non-zero shares + └── when pending payment is zero + │ └── it should revert ✅ + └── when pending payment is not zero + └── it should update released mapping for the account by pending payment ✅ + └── it should update total released by pending payment ✅ + └── it should send pending payment amount of native tokens to account ✅ + └── it should emit PaymentReleased event ✅ + \ No newline at end of file diff --git a/src/test/split-BTT/set-contract-uri/setContractURI.t.sol b/src/test/split-BTT/set-contract-uri/setContractURI.t.sol new file mode 100644 index 000000000..4a9da18b4 --- /dev/null +++ b/src/test/split-BTT/set-contract-uri/setContractURI.t.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.11; + +import "../../utils/BaseTest.sol"; + +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +contract MySplit is Split {} + +contract SplitTest_SetContractURI is BaseTest { + address payable public implementation; + address payable public proxy; + + address[] public payees; + uint256[] public shares; + + address internal caller; + string internal _contractURI; + + MySplit internal splitContract; + + function setUp() public override { + super.setUp(); + + // Deploy implementation. + implementation = payable(address(new MySplit())); + + // create 5 payees and shares + for (uint160 i = 0; i < 5; i++) { + payees.push(getActor(i + 100)); + shares.push(i + 100); + } + + caller = getActor(1); + + // Deploy proxy pointing to implementaion. + vm.prank(deployer); + proxy = payable( + address( + new TWProxy( + implementation, + abi.encodeCall(Split.initialize, (deployer, CONTRACT_URI, forwarders(), payees, shares)) + ) + ) + ); + + splitContract = MySplit(proxy); + _contractURI = "ipfs://contracturi"; + } + + function test_setContractURI_callerNotAuthorized() public { + vm.prank(address(caller)); + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + TWStrings.toHexString(uint160(caller), 20), + " is missing role ", + TWStrings.toHexString(uint256(0), 32) + ) + ); + splitContract.setContractURI(_contractURI); + } + + modifier whenCallerAuthorized() { + vm.prank(deployer); + splitContract.grantRole(bytes32(0x00), caller); + _; + } + + function test_setContractURI_empty() public whenCallerAuthorized { + vm.prank(address(caller)); + splitContract.setContractURI(""); + + // get contract uri + assertEq(splitContract.contractURI(), ""); + } + + function test_setContractURI_notEmpty() public whenCallerAuthorized { + vm.prank(address(caller)); + splitContract.setContractURI(_contractURI); + + // get contract uri + assertEq(splitContract.contractURI(), _contractURI); + } +} diff --git a/src/test/split-BTT/set-contract-uri/setContractURI.tree b/src/test/split-BTT/set-contract-uri/setContractURI.tree new file mode 100644 index 000000000..8fc480b19 --- /dev/null +++ b/src/test/split-BTT/set-contract-uri/setContractURI.tree @@ -0,0 +1,8 @@ +setContractURI(string calldata _uri) +├── when caller not authorized + │ └── it should revert ✅ + └── when caller is authorized + └── when `uri` is empty + │ └── it should update contract URI to empty string ✅ + └── when `uri` is not empty + └── it should update contract URI to `_uri` ✅ \ No newline at end of file