Skip to content

Commit 6c84ade

Browse files
authored
Add NttNormalizer abstract contract (#140)
* Add NttNormalizer abstract contract * Bugfix for current capacity calculation * Reorder checks in normalizedAmount.scale
1 parent 2f2bea4 commit 6c84ade

File tree

5 files changed

+60
-27
lines changed

5 files changed

+60
-27
lines changed

src/Manager.sol

+12-23
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ import "./libraries/external/ReentrancyGuardUpgradeable.sol";
1313
import "./libraries/EndpointStructs.sol";
1414
import "./libraries/EndpointHelpers.sol";
1515
import "./libraries/RateLimiter.sol";
16-
import "./libraries/NormalizedAmount.sol";
1716
import "./interfaces/IManager.sol";
1817
import "./interfaces/IManagerEvents.sol";
1918
import "./interfaces/INTTToken.sol";
2019
import "./Endpoint.sol";
2120
import "./EndpointRegistry.sol";
21+
import "./NttNormalizer.sol";
2222
import "./libraries/PausableOwnable.sol";
2323

2424
// TODO: rename this (it's really the business logic)
@@ -27,13 +27,12 @@ abstract contract Manager is
2727
IManagerEvents,
2828
EndpointRegistry,
2929
RateLimiter,
30+
NttNormalizer,
3031
ReentrancyGuardUpgradeable,
3132
PausableOwnable
3233
{
3334
using BytesParsing for bytes;
3435
using SafeERC20 for IERC20;
35-
using NormalizedAmountLib for uint256;
36-
using NormalizedAmountLib for NormalizedAmount;
3736

3837
error RefundFailed(uint256 refundAmount);
3938
error CannotRenounceManagerOwnership(address owner);
@@ -100,7 +99,7 @@ abstract contract Manager is
10099
Mode _mode,
101100
uint16 _chainId,
102101
uint64 _rateLimitDuration
103-
) RateLimiter(_rateLimitDuration) {
102+
) RateLimiter(_rateLimitDuration) NttNormalizer(_token) {
104103
token = _token;
105104
mode = _mode;
106105
chainId = _chainId;
@@ -163,15 +162,11 @@ abstract contract Manager is
163162
}
164163

165164
function setOutboundLimit(uint256 limit) external onlyOwner {
166-
uint8 decimals = _tokenDecimals();
167-
NormalizedAmount memory normalized = NormalizedAmountLib.normalize(limit, decimals);
168-
_setOutboundLimit(normalized);
165+
_setOutboundLimit(nttNormalize(limit));
169166
}
170167

171168
function setInboundLimit(uint256 limit, uint16 chainId_) external onlyOwner {
172-
uint8 decimals = _tokenDecimals();
173-
NormalizedAmount memory normalized = NormalizedAmountLib.normalize(limit, decimals);
174-
_setInboundLimit(normalized, chainId_);
169+
_setInboundLimit(nttNormalize(limit), chainId_);
175170
}
176171

177172
function completeOutboundQueuedTransfer(uint64 messageSequence)
@@ -225,12 +220,9 @@ abstract contract Manager is
225220
{
226221
NormalizedAmount memory normalizedAmount;
227222
{
228-
// query tokens decimals
229-
uint8 decimals = _tokenDecimals();
230-
231-
normalizedAmount = amount.normalize(decimals);
223+
normalizedAmount = nttNormalize(amount);
232224
// don't deposit dust that can not be bridged due to the decimal shift
233-
uint256 newAmount = normalizedAmount.denormalize(decimals);
225+
uint256 newAmount = nttDenormalize(normalizedAmount);
234226
if (amount != newAmount) {
235227
revert TransferAmountHasDust(amount, amount - newAmount);
236228
}
@@ -420,7 +412,7 @@ abstract contract Manager is
420412
recipientChain, priceQuotes, sortedInstructions, encodedManagerPayload
421413
);
422414

423-
emit TransferSent(recipient, amount.denormalize(_tokenDecimals()), recipientChain, sequence);
415+
emit TransferSent(recipient, nttDenormalize(amount), recipientChain, sequence);
424416

425417
// return the sequence number
426418
return sequence;
@@ -481,7 +473,7 @@ abstract contract Manager is
481473
revert InvalidTargetChain(nativeTokenTransfer.toChain, chainId);
482474
}
483475

484-
NormalizedAmount memory nativeTransferAmount = nativeTokenTransfer.amount;
476+
NormalizedAmount memory nativeTransferAmount = nttFixDecimals(nativeTokenTransfer.amount);
485477

486478
address transferRecipient = fromWormholeFormat(nativeTokenTransfer.to);
487479

@@ -527,10 +519,8 @@ abstract contract Manager is
527519

528520
function _mintOrUnlockToRecipient(address recipient, NormalizedAmount memory amount) internal {
529521
// calculate proper amount of tokens to unlock/mint to recipient
530-
// query the decimals of the token contract that's tied to this manager
531-
// adjust the decimals of the amount in the nativeTokenTransfer payload accordingly
532-
uint8 decimals = _tokenDecimals();
533-
uint256 denormalizedAmount = amount.denormalize(decimals);
522+
// denormalize the amount
523+
uint256 denormalizedAmount = nttDenormalize(amount);
534524

535525
if (mode == Mode.LOCKING) {
536526
// unlock tokens to the specified recipient
@@ -585,7 +575,6 @@ abstract contract Manager is
585575
}
586576

587577
function _tokenDecimals() internal view override returns (uint8) {
588-
(, bytes memory queriedDecimals) = token.staticcall(abi.encodeWithSignature("decimals()"));
589-
return abi.decode(queriedDecimals, (uint8));
578+
return tokenDecimals;
590579
}
591580
}

src/NttNormalizer.sol

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// SPDX-License-Identifier: Apache 2
2+
pragma solidity >=0.8.8 <0.9.0;
3+
4+
import "./libraries/NormalizedAmount.sol";
5+
6+
abstract contract NttNormalizer {
7+
using NormalizedAmountLib for uint256;
8+
using NormalizedAmountLib for NormalizedAmount;
9+
10+
uint8 immutable tokenDecimals;
11+
12+
constructor(address _token) {
13+
tokenDecimals = _tokenDecimals(_token);
14+
}
15+
16+
function _tokenDecimals(address token) internal view returns (uint8) {
17+
(, bytes memory queriedDecimals) = token.staticcall(abi.encodeWithSignature("decimals()"));
18+
return abi.decode(queriedDecimals, (uint8));
19+
}
20+
21+
function nttNormalize(uint256 amount) public view returns (NormalizedAmount memory) {
22+
return amount.normalize(tokenDecimals);
23+
}
24+
25+
function nttDenormalize(NormalizedAmount memory amount) public view returns (uint256) {
26+
return amount.denormalize(tokenDecimals);
27+
}
28+
29+
/// @dev Shift decimals of `amount` to match the token decimals
30+
function nttFixDecimals(NormalizedAmount memory amount)
31+
public
32+
view
33+
returns (NormalizedAmount memory)
34+
{
35+
return nttNormalize(nttDenormalize(amount));
36+
}
37+
}

src/libraries/NormalizedAmount.sol

+5-1
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,16 @@ library NormalizedAmountLib {
132132
return a.amount < b.amount ? a : b;
133133
}
134134

135-
/// @dev scale the amount from origDecimals to normDecimals (base 10)
135+
/// @dev scale the amount from original decimals to target decimals (base 10)
136136
function scale(
137137
uint256 amount,
138138
uint8 fromDecimals,
139139
uint8 toDecimals
140140
) internal pure returns (uint256) {
141+
if (fromDecimals == toDecimals) {
142+
return amount;
143+
}
144+
141145
if (fromDecimals > toDecimals) {
142146
return amount / (10 ** (fromDecimals - toDecimals));
143147
} else {

src/libraries/RateLimiter.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ abstract contract RateLimiter is IRateLimiter, IRateLimiterEvents {
180180
difference = oldLimit.sub(newLimit);
181181
newCurrentCapacity = currentCapacity.gt(difference)
182182
? currentCapacity.sub(difference)
183-
: NormalizedAmount(0, 0);
183+
: NormalizedAmount(0, currentCapacity.decimals);
184184
} else {
185185
difference = newLimit.sub(oldLimit);
186186
newCurrentCapacity = currentCapacity.add(difference);

test/Manager.t.sol

+5-2
Original file line numberDiff line numberDiff line change
@@ -446,14 +446,17 @@ contract TestManager is Test, IManagerEvents, IRateLimiterEvents {
446446
// === storage
447447

448448
function test_noAutomaticSlot() public {
449-
ManagerContract c = new ManagerContract(address(0x123), Manager.Mode.LOCKING, 1, 1 days);
449+
DummyToken t = new DummyToken();
450+
ManagerContract c = new ManagerContract(address(t), Manager.Mode.LOCKING, 1, 1 days);
450451
assertEq(c.lastSlot(), 0x0);
451452
}
452453

453454
function test_constructor() public {
455+
DummyToken t = new DummyToken();
456+
454457
vm.startStateDiffRecording();
455458

456-
new ManagerStandalone(address(0x123), Manager.Mode.LOCKING, 1, 1 days);
459+
new ManagerStandalone(address(t), Manager.Mode.LOCKING, 1, 1 days);
457460

458461
Utils.assertSafeUpgradeableConstructor(vm.stopAndReturnStateDiff());
459462
}

0 commit comments

Comments
 (0)