-
Notifications
You must be signed in to change notification settings - Fork 224
/
Copy pathPayments.sol
140 lines (126 loc) · 5.89 KB
/
Payments.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
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {Constants} from '../libraries/Constants.sol';
import {PaymentsImmutables} from '../modules/PaymentsImmutables.sol';
import {SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol';
import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {ERC721} from 'solmate/src/tokens/ERC721.sol';
import {ERC1155} from 'solmate/src/tokens/ERC1155.sol';
/// @title Payments contract
/// @notice Performs various operations around the payment of ETH and tokens
abstract contract Payments is PaymentsImmutables {
using SafeTransferLib for ERC20;
using SafeTransferLib for address;
error InsufficientToken();
error InsufficientETH();
error InvalidBips();
error InvalidSpender();
uint256 internal constant FEE_BIPS_BASE = 10_000;
/// @notice Pays an amount of ETH or ERC20 to a recipient
/// @param token The token to pay (can be ETH using Constants.ETH)
/// @param recipient The address that will receive the payment
/// @param value The amount to pay
function pay(address token, address recipient, uint256 value) internal {
if (token == Constants.ETH) {
recipient.safeTransferETH(value);
} else {
if (value == Constants.CONTRACT_BALANCE) {
value = ERC20(token).balanceOf(address(this));
}
ERC20(token).safeTransfer(recipient, value);
}
}
/// @notice Approves a protocol to spend ERC20s in the router
/// @param token The token to approve
/// @param spender Which protocol to approve
function approveERC20(ERC20 token, Spenders spender) internal {
// check spender is one of our approved spenders
address spenderAddress;
/// @dev use 0 = Opensea Conduit for both Seaport v1.4 and v1.5
if (spender == Spenders.OSConduit) spenderAddress = OPENSEA_CONDUIT;
else if (spender == Spenders.Sudoswap) spenderAddress = SUDOSWAP;
else revert InvalidSpender();
// set approval
token.safeApprove(spenderAddress, type(uint256).max);
}
/// @notice Pays a proportion of the contract's ETH or ERC20 to a recipient
/// @param token The token to pay (can be ETH using Constants.ETH)
/// @param recipient The address that will receive payment
/// @param bips Portion in bips of whole balance of the contract
function payPortion(address token, address recipient, uint256 bips) internal {
if (bips == 0 || bips > FEE_BIPS_BASE) revert InvalidBips();
if (token == Constants.ETH) {
uint256 balance = address(this).balance;
uint256 amount = (balance * bips) / FEE_BIPS_BASE;
recipient.safeTransferETH(amount);
} else {
uint256 balance = ERC20(token).balanceOf(address(this));
uint256 amount = (balance * bips) / FEE_BIPS_BASE;
ERC20(token).safeTransfer(recipient, amount);
}
}
/// @notice Sweeps all of the contract's ERC20 or ETH to an address
/// @param token The token to sweep (can be ETH using Constants.ETH)
/// @param recipient The address that will receive payment
/// @param amountMinimum The minimum desired amount
function sweep(address token, address recipient, uint256 amountMinimum) internal {
uint256 balance;
if (token == Constants.ETH) {
balance = address(this).balance;
if (balance < amountMinimum) revert InsufficientETH();
if (balance > 0) recipient.safeTransferETH(balance);
} else {
balance = ERC20(token).balanceOf(address(this));
if (balance < amountMinimum) revert InsufficientToken();
if (balance > 0) ERC20(token).safeTransfer(recipient, balance);
}
}
/// @notice Sweeps an ERC721 to a recipient from the contract
/// @param token The ERC721 token to sweep
/// @param recipient The address that will receive payment
/// @param id The ID of the ERC721 to sweep
function sweepERC721(address token, address recipient, uint256 id) internal {
ERC721(token).safeTransferFrom(address(this), recipient, id);
}
/// @notice Sweeps all of the contract's ERC1155 to an address
/// @param token The ERC1155 token to sweep
/// @param recipient The address that will receive payment
/// @param id The ID of the ERC1155 to sweep
/// @param amountMinimum The minimum desired amount
function sweepERC1155(address token, address recipient, uint256 id, uint256 amountMinimum) internal {
uint256 balance = ERC1155(token).balanceOf(address(this), id);
if (balance < amountMinimum) revert InsufficientToken();
ERC1155(token).safeTransferFrom(address(this), recipient, id, balance, bytes(''));
}
/// @notice Wraps an amount of ETH into WETH
/// @param recipient The recipient of the WETH
/// @param amount The amount to wrap (can be CONTRACT_BALANCE)
function wrapETH(address recipient, uint256 amount) internal {
if (amount == Constants.CONTRACT_BALANCE) {
amount = address(this).balance;
} else if (amount > address(this).balance) {
revert InsufficientETH();
}
if (amount > 0) {
WETH9.deposit{value: amount}();
if (recipient != address(this)) {
WETH9.transfer(recipient, amount);
}
}
}
/// @notice Unwraps all of the contract's WETH into ETH
/// @param recipient The recipient of the ETH
/// @param amountMinimum The minimum amount of ETH desired
function unwrapWETH9(address recipient, uint256 amountMinimum) internal {
uint256 value = WETH9.balanceOf(address(this));
if (value < amountMinimum) {
revert InsufficientETH();
}
if (value > 0) {
WETH9.withdraw(value);
if (recipient != address(this)) {
recipient.safeTransferETH(value);
}
}
}
}