-
Notifications
You must be signed in to change notification settings - Fork 3
/
PushSplit.sol
148 lines (123 loc) · 5.77 KB
/
PushSplit.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.23;
import { SplitV2Lib } from "../../libraries/SplitV2.sol";
import { SplitWalletV2 } from "../SplitWalletV2.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";
/**
* @title Push Split Wallet
* @author Splits
* @notice The implementation logic for a splitter that distributes directly to the recipients.
* @dev `SplitProxy` handles `receive()` itself to avoid the gas cost with `DELEGATECALL`.
*/
contract PushSplit is SplitWalletV2 {
using SplitV2Lib for SplitV2Lib.Split;
using SafeERC20 for IERC20;
using SafeTransferLib for address;
/* -------------------------------------------------------------------------- */
/* CONSTRUCTOR & INITIALIZER */
/* -------------------------------------------------------------------------- */
constructor(address _splitWarehouse) SplitWalletV2(_splitWarehouse) { }
/* -------------------------------------------------------------------------- */
/* PUBLIC/EXTERNAL FUNCTIONS */
/* -------------------------------------------------------------------------- */
/**
* @notice Distributes the tokens in the split & Warehouse to the recipients.
* @dev The split must be initialized and the hash of _split must match splitHash.
* @param _split The split struct containing the split data that gets distributed.
* @param _token The token to distribute.
* @param _distributor The distributor of the split.
*/
function distribute(
SplitV2Lib.Split calldata _split,
address _token,
address _distributor
)
external
override
pausable
{
if (splitHash != _split.getHash()) revert InvalidSplit();
(uint256 splitBalance, uint256 warehouseBalance) = getSplitBalance(_token);
if (warehouseBalance > 1) withdrawFromWarehouse(_token);
// @solidity memory-safe-assembly
assembly {
// splitBalance -= uint(splitBalance > 0);
splitBalance := sub(splitBalance, iszero(iszero(splitBalance)))
// warehouseBalance -= uint(warehouseBalance > 0);
warehouseBalance := sub(warehouseBalance, iszero(iszero(warehouseBalance)))
}
_distribute({
_split: _split,
_token: _token,
_amount: warehouseBalance + splitBalance,
_distributor: _distributor
});
}
/**
* @notice Distributes a specific amount of tokens in the split & Warehouse to the recipients.
* @dev The split must be initialized and the hash of _split must match splitHash.
* @dev Will revert if the amount of tokens to transfer or distribute doesn't exist.
* @param _split The split struct containing the split data that gets distributed.
* @param _token The token to distribute.
* @param _distributeAmount The amount of tokens to distribute.
* @param _performWarehouseTransfer if true, withdraws all but 1 amount of tokens from the warehouse.
* @param _distributor The distributor of the split.
*/
function distribute(
SplitV2Lib.Split calldata _split,
address _token,
uint256 _distributeAmount,
bool _performWarehouseTransfer,
address _distributor
)
external
override
pausable
{
if (splitHash != _split.getHash()) revert InvalidSplit();
if (_performWarehouseTransfer) withdrawFromWarehouse(_token);
_distribute({ _split: _split, _token: _token, _amount: _distributeAmount, _distributor: _distributor });
}
/**
* @notice Withdraws tokens from the warehouse to the split wallet.
* @param _token The token to withdraw.
*/
function withdrawFromWarehouse(address _token) public {
SPLITS_WAREHOUSE.withdraw(address(this), _token);
}
/* -------------------------------------------------------------------------- */
/* INTERNAL/PRIVATE */
/* -------------------------------------------------------------------------- */
/// @dev Assumes the amount is already present in the split wallet.
function _distribute(
SplitV2Lib.Split calldata _split,
address _token,
uint256 _amount,
address _distributor
)
internal
{
uint256 allocatedAmount;
uint256 numOfRecipients = _split.recipients.length;
uint256 distributorReward = _split.calculateDistributorReward(_amount);
uint256 amountToDistribute = _amount - distributorReward;
if (_token == NATIVE_TOKEN) {
for (uint256 i; i < numOfRecipients; ++i) {
allocatedAmount = _split.calculateAllocatedAmount(amountToDistribute, i);
if (!_split.recipients[i].trySafeTransferETH(allocatedAmount, SafeTransferLib.GAS_STIPEND_NO_GRIEF)) {
SPLITS_WAREHOUSE.deposit{ value: allocatedAmount }(_split.recipients[i], _token, allocatedAmount);
}
}
if (distributorReward > 0) _distributor.safeTransferETH(distributorReward);
} else {
for (uint256 i; i < numOfRecipients; ++i) {
allocatedAmount = _split.calculateAllocatedAmount(amountToDistribute, i);
IERC20(_token).safeTransfer(_split.recipients[i], allocatedAmount);
}
if (distributorReward > 0) IERC20(_token).safeTransfer(_distributor, distributorReward);
}
emit SplitDistributed({ token: _token, distributor: _distributor, amount: _amount });
}
}