-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(contracts): make
MultiPayment
contract upgradable and emit…
… payment events (#858) * support partial payments and set gas limit * tests * add revert workaround: https://book.getfoundry.sh/cheatcodes/expect-revert * make MultiPayment upgradeable * deploy proxy * regenerate genesis block * update tests * style: resolve style guide violations * Update contracts/src/multi-payment/MultiPaymentV1.sol Co-authored-by: Sebastijan K. <[email protected]> * Update contracts/test/multi-payment/MultiPayment.sol Co-authored-by: Sebastijan K. <[email protected]> * Update contracts/test/multi-payment/MultiPayment.sol Co-authored-by: Sebastijan K. <[email protected]> * style: resolve style guide violations * fix --------- Co-authored-by: Sebastijan K. <[email protected]>
- Loading branch information
1 parent
43b1eeb
commit 1fd3832
Showing
25 changed files
with
3,500 additions
and
2,899 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// SPDX-License-Identifier: GNU GENERAL PUBLIC LICENSE | ||
pragma solidity ^0.8.27; | ||
|
||
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; | ||
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; | ||
|
||
contract MultiPaymentV1 is UUPSUpgradeable, OwnableUpgradeable { | ||
error RecipientsAndAmountsMismatch(); | ||
error InvalidValue(); | ||
|
||
event Payment(address indexed recipient, uint256 amount, bool success); | ||
|
||
// Initializers | ||
function initialize() public initializer { | ||
__Ownable_init(msg.sender); | ||
} | ||
|
||
// Overrides | ||
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} | ||
|
||
function version() external pure returns (uint256) { | ||
return 1; | ||
} | ||
|
||
function pay(address payable[] calldata recipients, uint256[] calldata amounts) external payable { | ||
if (recipients.length != amounts.length) { | ||
revert RecipientsAndAmountsMismatch(); | ||
} | ||
|
||
// Ensure value sent is equal to the total amount to send | ||
uint256 total = 0; | ||
for (uint256 i = 0; i < amounts.length; i++) { | ||
total += amounts[i]; | ||
} | ||
if (msg.value != total) { | ||
revert InvalidValue(); | ||
} | ||
|
||
if (recipients.length == 0) { | ||
return; | ||
} | ||
|
||
for (uint256 i = 0; i < recipients.length; i++) { | ||
(bool sent,) = recipients[i].call{value: amounts[i], gas: 5000}(""); | ||
if (sent) { | ||
total -= amounts[i]; | ||
} | ||
|
||
emit Payment(recipients[i], amounts[i], sent); | ||
} | ||
|
||
// Refund any remaining value due to partial payments | ||
if (total > 0) { | ||
(bool success,) = msg.sender.call{value: total}(""); | ||
require(success, "Refund failed"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// SPDX-License-Identifier: GNU GENERAL PUBLIC LICENSE | ||
pragma solidity ^0.8.13; | ||
|
||
import {Test, console} from "@forge-std/Test.sol"; | ||
import {MultiPaymentV1} from "@contracts/multi-payment/MultiPaymentV1.sol"; | ||
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; | ||
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; | ||
|
||
contract MultiPaymentV2Test is MultiPaymentV1 { | ||
function versionv2() external pure returns (uint256) { | ||
return 2; | ||
} | ||
} | ||
|
||
contract ProxyTest is Test { | ||
MultiPaymentV1 public multiPayment; | ||
|
||
function setUp() public { | ||
bytes memory data = abi.encode(MultiPaymentV1.initialize.selector); | ||
address proxy = address(new ERC1967Proxy(address(new MultiPaymentV1()), data)); | ||
multiPayment = MultiPaymentV1(proxy); | ||
} | ||
|
||
function test_initialize_should_revert() public { | ||
vm.expectRevert(Initializable.InvalidInitialization.selector); | ||
multiPayment.initialize(); | ||
} | ||
|
||
function test_should_have_valid_UPGRADE_INTERFACE_VERSION() public view { | ||
assertEq(multiPayment.UPGRADE_INTERFACE_VERSION(), "5.0.0"); | ||
} | ||
|
||
function test_proxy_should_update() public { | ||
assertEq(multiPayment.version(), 1); | ||
assertEq(multiPayment.UPGRADE_INTERFACE_VERSION(), "5.0.0"); | ||
multiPayment.upgradeToAndCall(address(new MultiPaymentV2Test()), bytes("")); | ||
|
||
// Cast proxy to new contract | ||
MultiPaymentV2Test multiPaymentNew = MultiPaymentV2Test(address(multiPayment)); | ||
assertEq(multiPaymentNew.versionv2(), 2); | ||
|
||
// Should keep old data | ||
vm.expectRevert(Initializable.InvalidInitialization.selector); | ||
multiPaymentNew.initialize(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.