@1inch/solidity-utils 2.0.17 → 2.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contracts/libraries/ECDSA.sol +216 -0
- package/contracts/libraries/RevertReasonForwarder.sol +5 -3
- package/contracts/libraries/RevertReasonParser.sol +13 -9
- package/contracts/libraries/SafeERC20.sol +3 -3
- package/contracts/libraries/StringUtil.sol +2 -2
- package/dist/src/prelude.js +3 -0
- package/dist/src/prelude.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.0;
|
|
4
|
+
pragma abicoder v1;
|
|
5
|
+
|
|
6
|
+
import "@openzeppelin/contracts/interfaces/IERC1271.sol";
|
|
7
|
+
|
|
8
|
+
library ECDSA {
|
|
9
|
+
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns(address signer) {
|
|
10
|
+
/// @solidity memory-safe-assembly
|
|
11
|
+
assembly { // solhint-disable-line no-inline-assembly
|
|
12
|
+
let ptr := mload(0x40)
|
|
13
|
+
|
|
14
|
+
mstore(ptr, hash)
|
|
15
|
+
mstore(add(ptr, 0x20), v)
|
|
16
|
+
mstore(add(ptr, 0x40), r)
|
|
17
|
+
mstore(add(ptr, 0x60), s)
|
|
18
|
+
if staticcall(gas(), 0x1, ptr, 0x80, 0, 0x20) {
|
|
19
|
+
signer := mload(0)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns(address signer) {
|
|
25
|
+
/// @solidity memory-safe-assembly
|
|
26
|
+
assembly { // solhint-disable-line no-inline-assembly
|
|
27
|
+
let ptr := mload(0x40)
|
|
28
|
+
|
|
29
|
+
mstore(ptr, hash)
|
|
30
|
+
mstore(add(ptr, 0x20), add(27, shr(255, vs)))
|
|
31
|
+
mstore(add(ptr, 0x40), r)
|
|
32
|
+
mstore(add(ptr, 0x60), shr(1, shl(1, vs)))
|
|
33
|
+
if staticcall(gas(), 0x1, ptr, 0x80, 0, 0x20) {
|
|
34
|
+
signer := mload(0)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function recover(bytes32 hash, bytes calldata signature) internal view returns(address signer) {
|
|
40
|
+
/// @solidity memory-safe-assembly
|
|
41
|
+
assembly { // solhint-disable-line no-inline-assembly
|
|
42
|
+
let ptr := mload(0x40)
|
|
43
|
+
|
|
44
|
+
// memory[ptr:ptr+0x80] = (hash, v, r, s)
|
|
45
|
+
switch signature.length
|
|
46
|
+
case 65 {
|
|
47
|
+
// memory[ptr+0x20:ptr+0x80] = (v, r, s)
|
|
48
|
+
mstore(add(ptr, 0x20), byte(0, calldataload(add(signature.offset, 0x40))))
|
|
49
|
+
calldatacopy(add(ptr, 0x40), signature.offset, 0x40)
|
|
50
|
+
}
|
|
51
|
+
case 64 {
|
|
52
|
+
// memory[ptr+0x20:ptr+0x80] = (v, r, s)
|
|
53
|
+
let vs := calldataload(add(signature.offset, 0x20))
|
|
54
|
+
mstore(add(ptr, 0x20), add(27, shr(255, vs)))
|
|
55
|
+
calldatacopy(add(ptr, 0x40), signature.offset, 0x20)
|
|
56
|
+
mstore(add(ptr, 0x60), shr(1, shl(1, vs)))
|
|
57
|
+
}
|
|
58
|
+
default {
|
|
59
|
+
ptr := 0
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if ptr {
|
|
63
|
+
if gt(mload(add(ptr, 0x60)), 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
|
|
64
|
+
ptr := 0
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if ptr {
|
|
68
|
+
// memory[ptr:ptr+0x20] = (hash)
|
|
69
|
+
mstore(ptr, hash)
|
|
70
|
+
|
|
71
|
+
if staticcall(gas(), 0x1, ptr, 0x80, 0, 0x20) {
|
|
72
|
+
signer := mload(0)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function recoverOrIsValidSignature(address signer, bytes32 hash, bytes calldata signature) internal view returns(bool success) {
|
|
80
|
+
if ((signature.length == 64 || signature.length == 65) && recover(hash, signature) == signer) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
return isValidSignature(signer, hash, signature);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function recoverOrIsValidSignature(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns(bool success) {
|
|
87
|
+
if (recover(hash, v, r, s) == signer) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return isValidSignature(signer, hash, v, r, s);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function recoverOrIsValidSignature(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns(bool success) {
|
|
94
|
+
if (recover(hash, r, vs) == signer) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
return isValidSignature(signer, hash, r, vs);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function recoverOrIsValidSignature65(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns(bool success) {
|
|
101
|
+
if (recover(hash, r, vs) == signer) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return isValidSignature65(signer, hash, r, vs);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function isValidSignature(address signer, bytes32 hash, bytes calldata signature) internal view returns(bool success) {
|
|
108
|
+
// (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature));
|
|
109
|
+
// return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
|
|
110
|
+
bytes4 selector = IERC1271.isValidSignature.selector;
|
|
111
|
+
/// @solidity memory-safe-assembly
|
|
112
|
+
assembly { // solhint-disable-line no-inline-assembly
|
|
113
|
+
let ptr := mload(0x40)
|
|
114
|
+
let len := add(0x64, signature.length)
|
|
115
|
+
|
|
116
|
+
mstore(ptr, selector)
|
|
117
|
+
mstore(add(ptr, 0x04), hash)
|
|
118
|
+
mstore(add(ptr, 0x24), 0x40)
|
|
119
|
+
mstore(add(ptr, 0x44), signature.length)
|
|
120
|
+
calldatacopy(add(ptr, 0x64), signature.offset, signature.length)
|
|
121
|
+
mstore(0, 0)
|
|
122
|
+
if staticcall(gas(), signer, ptr, len, 0, 0x20) {
|
|
123
|
+
success := eq(selector, mload(0))
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function isValidSignature(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns(bool success) {
|
|
129
|
+
bytes4 selector = IERC1271.isValidSignature.selector;
|
|
130
|
+
/// @solidity memory-safe-assembly
|
|
131
|
+
assembly { // solhint-disable-line no-inline-assembly
|
|
132
|
+
let ptr := mload(0x40)
|
|
133
|
+
let len := add(0x64, 65)
|
|
134
|
+
|
|
135
|
+
mstore(ptr, selector)
|
|
136
|
+
mstore(add(ptr, 0x04), hash)
|
|
137
|
+
mstore(add(ptr, 0x24), 0x40)
|
|
138
|
+
mstore(add(ptr, 0x44), 65)
|
|
139
|
+
mstore(add(ptr, 0x64), r)
|
|
140
|
+
mstore(add(ptr, 0x84), s)
|
|
141
|
+
mstore8(add(ptr, 0xa4), v)
|
|
142
|
+
mstore(0, 0)
|
|
143
|
+
if staticcall(gas(), signer, ptr, len, 0, 0x20) {
|
|
144
|
+
success := eq(selector, mload(0))
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function isValidSignature(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns(bool success) {
|
|
150
|
+
// (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, abi.encodePacked(r, vs)));
|
|
151
|
+
// return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
|
|
152
|
+
bytes4 selector = IERC1271.isValidSignature.selector;
|
|
153
|
+
/// @solidity memory-safe-assembly
|
|
154
|
+
assembly { // solhint-disable-line no-inline-assembly
|
|
155
|
+
let ptr := mload(0x40)
|
|
156
|
+
let len := add(0x64, 64)
|
|
157
|
+
|
|
158
|
+
mstore(ptr, selector)
|
|
159
|
+
mstore(add(ptr, 0x04), hash)
|
|
160
|
+
mstore(add(ptr, 0x24), 0x40)
|
|
161
|
+
mstore(add(ptr, 0x44), 64)
|
|
162
|
+
mstore(add(ptr, 0x64), r)
|
|
163
|
+
mstore(add(ptr, 0x84), vs)
|
|
164
|
+
mstore(0, 0)
|
|
165
|
+
if staticcall(gas(), signer, ptr, len, 0, 0x20) {
|
|
166
|
+
success := eq(selector, mload(0))
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function isValidSignature65(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns(bool success) {
|
|
172
|
+
// (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, abi.encodePacked(r, vs & ~uint256(1 << 255), uint8(vs >> 255))));
|
|
173
|
+
// return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
|
|
174
|
+
bytes4 selector = IERC1271.isValidSignature.selector;
|
|
175
|
+
/// @solidity memory-safe-assembly
|
|
176
|
+
assembly { // solhint-disable-line no-inline-assembly
|
|
177
|
+
let ptr := mload(0x40)
|
|
178
|
+
let len := add(0x64, 65)
|
|
179
|
+
|
|
180
|
+
mstore(ptr, selector)
|
|
181
|
+
mstore(add(ptr, 0x04), hash)
|
|
182
|
+
mstore(add(ptr, 0x24), 0x40)
|
|
183
|
+
mstore(add(ptr, 0x44), 65)
|
|
184
|
+
mstore(add(ptr, 0x64), r)
|
|
185
|
+
mstore(add(ptr, 0x84), shr(1, shl(1, vs)))
|
|
186
|
+
mstore8(add(ptr, 0xa4), add(27, shr(255, vs)))
|
|
187
|
+
mstore(0, 0)
|
|
188
|
+
if staticcall(gas(), signer, ptr, len, 0, 0x20) {
|
|
189
|
+
success := eq(selector, mload(0))
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 res) {
|
|
195
|
+
// 32 is the length in bytes of hash, enforced by the type signature above
|
|
196
|
+
// return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
|
|
197
|
+
/// @solidity memory-safe-assembly
|
|
198
|
+
assembly { // solhint-disable-line no-inline-assembly
|
|
199
|
+
mstore(0, 0x19457468657265756d205369676e6564204d6573736167653a0a333200000000) // "\x19Ethereum Signed Message:\n32"
|
|
200
|
+
mstore(28, hash)
|
|
201
|
+
res := keccak256(0, 60)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 res) {
|
|
206
|
+
// return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
|
|
207
|
+
/// @solidity memory-safe-assembly
|
|
208
|
+
assembly { // solhint-disable-line no-inline-assembly
|
|
209
|
+
let ptr := mload(0x40)
|
|
210
|
+
mstore(ptr, 0x1901000000000000000000000000000000000000000000000000000000000000) // "\x19\x01"
|
|
211
|
+
mstore(add(ptr, 0x02), domainSeparator)
|
|
212
|
+
mstore(add(ptr, 0x22), structHash)
|
|
213
|
+
res := keccak256(ptr, 66)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -6,9 +6,11 @@ pragma abicoder v1;
|
|
|
6
6
|
library RevertReasonForwarder {
|
|
7
7
|
function reRevert() internal pure {
|
|
8
8
|
// bubble up revert reason from latest external call
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
/// @solidity memory-safe-assembly
|
|
10
|
+
assembly { // solhint-disable-line no-inline-assembly
|
|
11
|
+
let ptr := mload(0x40)
|
|
12
|
+
returndatacopy(ptr, 0, returndatasize())
|
|
13
|
+
revert(ptr, returndatasize())
|
|
12
14
|
}
|
|
13
15
|
}
|
|
14
16
|
}
|
|
@@ -25,15 +25,18 @@ library RevertReasonParser {
|
|
|
25
25
|
// https://solidity.readthedocs.io/en/latest/control-structures.html#revert
|
|
26
26
|
// We assume that revert reason is abi-encoded as Error(string)
|
|
27
27
|
bytes4 selector;
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
if (data.length >= 4) {
|
|
29
|
+
/// @solidity memory-safe-assembly
|
|
30
|
+
assembly { // solhint-disable-line no-inline-assembly
|
|
31
|
+
selector := mload(add(data, 0x20))
|
|
32
|
+
}
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
// 68 = 4-byte selector + 32 bytes offset + 32 bytes length
|
|
33
36
|
if (selector == _ERROR_SELECTOR && data.length >= 68) {
|
|
34
37
|
string memory reason;
|
|
35
|
-
|
|
36
|
-
assembly {
|
|
38
|
+
/// @solidity memory-safe-assembly
|
|
39
|
+
assembly { // solhint-disable-line no-inline-assembly
|
|
37
40
|
// 68 = 32 bytes data length + 4-byte selector + 32 bytes offset
|
|
38
41
|
reason := add(data, 68)
|
|
39
42
|
}
|
|
@@ -42,16 +45,17 @@ library RevertReasonParser {
|
|
|
42
45
|
also sometimes there is extra 32 bytes of zeros padded in the end:
|
|
43
46
|
https://github.com/ethereum/solidity/issues/10170
|
|
44
47
|
because of that we can't check for equality and instead check
|
|
45
|
-
that string length + extra 68 bytes is
|
|
48
|
+
that string length + extra 68 bytes is equal or greater than overall data length
|
|
46
49
|
*/
|
|
47
|
-
if (data.length
|
|
48
|
-
|
|
50
|
+
if (data.length >= 68 + bytes(reason).length) {
|
|
51
|
+
return string.concat(prefix, "Error(", reason, ")");
|
|
52
|
+
}
|
|
49
53
|
}
|
|
50
54
|
// 36 = 4-byte selector + 32 bytes integer
|
|
51
55
|
else if (selector == _PANIC_SELECTOR && data.length == 36) {
|
|
52
56
|
uint256 code;
|
|
53
|
-
|
|
54
|
-
assembly {
|
|
57
|
+
/// @solidity memory-safe-assembly
|
|
58
|
+
assembly { // solhint-disable-line no-inline-assembly
|
|
55
59
|
// 36 = 32 bytes data length + 4-byte selector
|
|
56
60
|
code := mload(add(data, 36))
|
|
57
61
|
}
|
|
@@ -20,9 +20,9 @@ library SafeERC20 {
|
|
|
20
20
|
function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {
|
|
21
21
|
bytes4 selector = token.transferFrom.selector;
|
|
22
22
|
bool success;
|
|
23
|
+
/// @solidity memory-safe-assembly
|
|
23
24
|
assembly { // solhint-disable-line no-inline-assembly
|
|
24
25
|
let data := mload(0x40)
|
|
25
|
-
mstore(0x40, add(data, 100))
|
|
26
26
|
|
|
27
27
|
mstore(data, selector)
|
|
28
28
|
mstore(add(data, 0x04), from)
|
|
@@ -84,9 +84,9 @@ library SafeERC20 {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
function _makeCall(IERC20 token, bytes4 selector, address to, uint256 amount) private returns(bool done) {
|
|
87
|
+
/// @solidity memory-safe-assembly
|
|
87
88
|
assembly { // solhint-disable-line no-inline-assembly
|
|
88
89
|
let data := mload(0x40)
|
|
89
|
-
mstore(0x40, add(data, 68))
|
|
90
90
|
|
|
91
91
|
mstore(data, selector)
|
|
92
92
|
mstore(add(data, 0x04), to)
|
|
@@ -103,10 +103,10 @@ library SafeERC20 {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
function _makeCalldataCall(IERC20 token, bytes4 selector, bytes calldata args) private returns(bool done) {
|
|
106
|
+
/// @solidity memory-safe-assembly
|
|
106
107
|
assembly { // solhint-disable-line no-inline-assembly
|
|
107
108
|
let len := add(4, args.length)
|
|
108
109
|
let data := mload(0x40)
|
|
109
|
-
mstore(0x40, add(data, len))
|
|
110
110
|
|
|
111
111
|
mstore(data, selector)
|
|
112
112
|
calldatacopy(add(data, 0x04), args.offset, args.length)
|
|
@@ -14,8 +14,8 @@ library StringUtil {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function toHex(bytes memory data) internal pure returns (string memory result) {
|
|
17
|
-
|
|
18
|
-
assembly {
|
|
17
|
+
/// @solidity memory-safe-assembly
|
|
18
|
+
assembly { // solhint-disable-line no-inline-assembly
|
|
19
19
|
function _toHex16(input) -> output {
|
|
20
20
|
output := or(
|
|
21
21
|
and(input, 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000),
|
package/dist/src/prelude.js
CHANGED
|
@@ -18,6 +18,9 @@ chai_1.default.use(chai_as_promised_1.default);
|
|
|
18
18
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
19
19
|
const { time: timeImpl } = require('@openzeppelin/test-helpers');
|
|
20
20
|
function toBN(num, base) {
|
|
21
|
+
if (typeof (num) === 'string' && num.startsWith('0x')) {
|
|
22
|
+
return new bn_js_1.default(num.substring(2), 16);
|
|
23
|
+
}
|
|
21
24
|
return new bn_js_1.default(num, base);
|
|
22
25
|
}
|
|
23
26
|
exports.toBN = toBN;
|
package/dist/src/prelude.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prelude.js","sourceRoot":"","sources":["../../src/prelude.ts"],"names":[],"mappings":";;;;AAAA,qDAAuF;
|
|
1
|
+
{"version":3,"file":"prelude.js","sourceRoot":"","sources":["../../src/prelude.ts"],"names":[],"mappings":";;;;AAAA,qDAAuF;AA4CnF,0FA5CW,gBAAS,OA4CX;AACT,+FA7CsB,qBAAc,OA6CtB;AACd,uFA9CsC,aAAM,OA8CtC;AACN,uFA/C8C,aAAM,OA+C9C;AACN,uFAhDsD,aAAM,OAgDtD;AACN,uFAjD8D,aAAM,OAiD9D;AAhDV,mBAAiB;AACjB,gFAA8C;AAC9C,2CAAmC;AACnC,0DAAuB;AAuBnB,aAvBG,eAAE,CAuBH;AAtBN,cAAI,CAAC,GAAG,CAAC,0BAAc,CAAC,CAAC;AACzB,8DAA8D;AAC9D,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;AAEjE,SAAgB,IAAI,CAAE,GAAoB,EAAE,IAAqB;IAC7D,IAAI,OAAM,CAAC,GAAG,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE;QAClD,OAAO,IAAI,eAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;KACvC;IACD,OAAO,IAAI,eAAE,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC7B,CAAC;AALD,oBAKC;AAEY,QAAA,SAAS,GAAG;IACrB,YAAY,EAAE,4CAA4C;IAC1D,WAAW,EAAE,4CAA4C;IACzD,YAAY,EAAE,oEAAoE;IAClF,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE;IACjE,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE;IAChE,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE;CAC3D,CAAC;AAaE,QAAA,IAAI,GAAS,QAAQ,CAAC;AAEnC,SAAgB,KAAK,CAAE,CAAS;IAC5B,OAAO,IAAI,CAAC,IAAA,kBAAK,EAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AACnC,CAAC;AAFD,sBAEC"}
|