-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathKSSmartIntentRouterAccounting.sol
More file actions
148 lines (125 loc) · 4.98 KB
/
KSSmartIntentRouterAccounting.sol
File metadata and controls
148 lines (125 loc) · 4.98 KB
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.0;
import {KSSmartIntentStorage} from './KSSmartIntentStorage.sol';
import {ActionData} from './types/ActionData.sol';
import {ERC20Data, ERC20DataLibrary} from './types/ERC20Data.sol';
import {ERC721Data, ERC721DataLibrary} from './types/ERC721Data.sol';
import {TokenData} from './types/TokenData.sol';
import {ManagementRescuable} from 'ks-common-sc/src/base/ManagementRescuable.sol';
import {IKSGenericForwarder} from 'ks-common-sc/src/interfaces/IKSGenericForwarder.sol';
import {PermitHelper} from 'ks-common-sc/src/libraries/token/PermitHelper.sol';
import {TokenHelper} from 'ks-common-sc/src/libraries/token/TokenHelper.sol';
import {ManagementPausable} from 'ks-common-sc/src/base/ManagementPausable.sol';
import {IERC721Receiver} from 'openzeppelin-contracts/contracts/interfaces/IERC721Receiver.sol';
abstract contract KSSmartIntentRouterAccounting is
KSSmartIntentStorage,
ManagementRescuable,
ManagementPausable
{
using TokenHelper for address;
using PermitHelper for address;
mapping(bytes32 => mapping(address => uint256)) public erc20Allowances;
uint256 internal constant ERC721_WILDCARD_TOKEN_ID = type(uint256).max;
uint256 internal constant ERC721_IDS_SEPARATOR = type(uint256).max;
/// @notice Set the tokens' allowances for the intent
function _approveTokens(bytes32 intentHash, TokenData calldata tokenData, address mainAddress)
internal
{
for (uint256 i = 0; i < tokenData.erc20Data.length; i++) {
ERC20Data calldata erc20Data = tokenData.erc20Data[i];
erc20Allowances[intentHash][erc20Data.token] = erc20Data.amount;
if (erc20Data.permitData.length > 0) {
erc20Data.token.callERC20Permit(mainAddress, erc20Data.permitData);
}
}
for (uint256 i = 0; i < tokenData.erc721Data.length; i++) {
ERC721Data calldata erc721Data = tokenData.erc721Data[i];
if (erc721Data.permitData.length > 0) {
erc721Data.token.callERC721Permit(erc721Data.tokenId, erc721Data.permitData);
}
}
}
/// @notice Transfer the tokens to this contract and update the allowances
function _collectTokens(
bytes32 intentHash,
address mainAddress,
address actionContract,
TokenData calldata tokenData,
ActionData calldata actionData,
IKSGenericForwarder _forwarder,
uint256[] memory fees
) internal checkLengths(actionData.erc20Ids.length, actionData.erc20Amounts.length) {
uint256 approvalFlags = actionData.approvalFlags;
for (uint256 i = 0; i < actionData.erc20Ids.length; i++) {
address token = tokenData.erc20Data[actionData.erc20Ids[i]].token;
_spentAllowance(intentHash, token, actionData.erc20Amounts[i]);
ERC20DataLibrary.collect(
token,
actionData.erc20Amounts[i],
mainAddress,
actionContract,
fees[i],
_checkFlag(approvalFlags, i),
_forwarder,
actionData.feeInfo.partnerFeeConfigs[i],
actionData.feeInfo.protocolRecipient
);
}
approvalFlags >>= actionData.erc20Ids.length;
uint256 wildcardSeprator = _parseWildcardSeprator(actionData.erc721Ids);
uint256 wildcardCursor = wildcardSeprator + 1;
for (uint256 i = 0; i < wildcardSeprator; i++) {
ERC721Data calldata erc721Data = tokenData.erc721Data[actionData.erc721Ids[i]];
address token = erc721Data.token;
bool approvalFlag = _checkFlag(approvalFlags, i);
if (erc721Data.tokenId != ERC721_WILDCARD_TOKEN_ID) {
ERC721DataLibrary.collect(
token, erc721Data.tokenId, mainAddress, actionContract, _forwarder, approvalFlag
);
} else {
ERC721DataLibrary.collect(
token,
actionData.erc721Ids[wildcardCursor++],
mainAddress,
actionContract,
_forwarder,
approvalFlag
);
}
}
}
function _parseWildcardSeprator(uint256[] calldata erc721Ids)
internal
pure
returns (uint256 wildcardSeprator)
{
wildcardSeprator = erc721Ids.length;
for (uint256 i = 0; i < erc721Ids.length; i++) {
if (erc721Ids[i] == ERC721_IDS_SEPARATOR) return i;
}
}
function _spentAllowance(bytes32 intentHash, address token, uint256 amount) internal {
uint256 allowance = erc20Allowances[intentHash][token];
if (allowance < amount) {
revert ERC20InsufficientIntentAllowance(intentHash, token, allowance, amount);
}
unchecked {
erc20Allowances[intentHash][token] = allowance - amount;
}
}
function onERC721Received(address, address, uint256, bytes calldata)
external
pure
returns (bytes4)
{
return IERC721Receiver.onERC721Received.selector;
}
function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
return interfaceId == type(IERC721Receiver).interfaceId || super.supportsInterface(interfaceId);
}
function _checkFlag(uint256 flag, uint256 index) internal pure returns (bool result) {
assembly ('memory-safe') {
result := and(shr(index, flag), 1)
}
}
}