Omniscia Swisscoast Audit

HLQTYToken Manual Review Findings

HLQTYToken Manual Review Findings

HLQ-01M: Inexistent Restrictions of Multi-Signature Wallet

Description:

The original LQTY implementation implements a one-year restriction before the multi-signature wallet behind the HLiquity team can transact tokens, however, this restriction has been eliminated from the HLQTYToken.

Impact:

Transfer restrictions in relation to the multi-signature wallet are not in place in the HLQTYToken implementation which we consider incorrect.

Example:

packages/contracts/contracts/HLQTY/HLQTYToken.sol
186function _transfer(address sender, address recipient, uint256 amount) internal {
187 require(sender != address(0), "ERC20: transfer from the zero address");
188 require(recipient != address(0), "ERC20: transfer to the zero address");
189
190 require(amount <= uint256(type(int64).max), "Amount exceeds int64 limits");
191
192 int64 safeAmount = int64(amount);
193
194 address currentTokenAddress = _getTokenAddress();
195
196 int responseCode = HederaTokenService.transferToken(currentTokenAddress, sender, recipient, safeAmount);
197
198 _checkResponse(responseCode);
199
200 emit TokenTransfer(currentTokenAddress, sender, recipient, safeAmount);
201}

Recommendation:

We advise this restriction to be imposed by deferring the actual minting of tokens until after a year has elapsed.

Alternatively, we advise the tokens to be escrowed by the HLQTYToken itself to permit staking and to be withdrawable after one year.

Alleviation (04618e407bddce5b22e9cadd787fd3334bd3afe6):

The Swisscoast team has specified that no approach suits their intended business case, and as such they have opted to lock the tokens on a trust basis.

We consider this exhibit acknowledged as a result.

HLQ-02M: Inefficient Auto-Renewal Period

Description:

The period in which the contract's rent will auto-renew is inefficient as it is not a whole time unit (i.e. X days).

Impact:

Due to the imprecise renew period of 8_000_000 presently defined, it might be difficult to properly synchronize funding the HLQTYToken account to pay for its rent.

Example:

packages/contracts/contracts/HLQTY/HLQTYToken.sol
111token.expiry = createAutoRenewExpiry(address(this), 8000000);

Recommendation:

We advise the defaultAutoRenewPeriod as defined in the SafeHTS contract to be utilized which is equivalent to 7_776_000, ensuring that the auto-renewal period cycles every 90 days in accordance to the Hedera blockchain limitation.

Alleviation (04618e407bddce5b22e9cadd787fd3334bd3afe6):

The auto-renewal period has been configured to 7_776_000 per our recommendation, optimizing the system's rent efficiency.

Description:

Due to the usage of the Hedera Token Service, the transfer-related restrictions that the original LQTYToken implemented cannot be replicated in the HLQTYToken or its underlying HTS implementation.

Impact:

Safety precautions presently imposed by the original LQTY token implementation are not imposed on the HLQTY token.

Example:

packages/contracts/contracts/HLQTY/HLQTYToken.sol
186function _transfer(address sender, address recipient, uint256 amount) internal {
187 require(sender != address(0), "ERC20: transfer from the zero address");
188 require(recipient != address(0), "ERC20: transfer to the zero address");
189
190 require(amount <= uint256(type(int64).max), "Amount exceeds int64 limits");
191
192 int64 safeAmount = int64(amount);
193
194 address currentTokenAddress = _getTokenAddress();
195
196 int responseCode = HederaTokenService.transferToken(currentTokenAddress, sender, recipient, safeAmount);
197
198 _checkResponse(responseCode);
199
200 emit TokenTransfer(currentTokenAddress, sender, recipient, safeAmount);
201}

Recommendation:

We advise the restrictions to be imposed via the HLQTYToken::_tranfser function. While these restrictions will not apply to interactions with the HTS token directly, they are meant to prevent accidental transfer of funds rather than secure the prohibited recipients.

Alleviation (04618e407b):

The Swisscoast team has not supplied an alleviation for this exhibit whereas their response document implies it has been fixed but contains incorrect content that relates to HLQ-02M.

We advise the Swisscoast team to revisit this exhibit and specify whether they intend to acknowledge or alleviate it.

Alleviation (30f7253f63):

The Swisscoast team re-evaluated this exhibit and opted to acknowledge it, stating that the token adheres to the same restrictions as the LQTY token.

This is not correct, as the transfer and transfer-from implementations of the LQTYToken apply different restrictions.

In any case, we consider this exhibit acknowledged as these restrictions cannot be reliably replicated due to usage of the Hedera Token Service and we consider this exhibit safely acknowledged based on the fact that the Swisscoast team will honestly operate the token.

HLQ-04M: Insecure Subtraction of Balances

Description:

The referenced subtraction performed will be unchecked due to the pragma version of the codebase.

Impact:

Although unlikely, any reduction of the address(this) balance will not be handled and will result in an overflow. While the transaction will properly revert in such a case, we still advise the overflow to be handled distinctly from the treasury-related error.

Example:

packages/contracts/contracts/HLQTY/HLQTYToken.sol
229if (
230 !((_balanceOf(address(this)) - balance) ==
231 amount)
232) revert('The smart contract is not the treasury account');

Recommendation:

We advise it to be safely performed by utilizing the SafeMath::sub function as exposed by the uint256 data type.

Alleviation (04618e407bddce5b22e9cadd787fd3334bd3afe6):

The codebase employs the SafeMath::sub function albeit using non-standard syntax (i.e. directly invoking the library function).

While the exhibit is addressed, we advise the using SafeMath for uint256 syntax to be employed increasing the legibility of the code.

HLQ-05M: Inexistent Bypass of Authorization

Description:

The original LQTY token implementation permitted the LQTYStaking implementation to bypass approvals by performing a direct transfer in the HLQTYToken::sendToHLQTYStaking function, however, the HLQTY implementation requires an approval due to the usage of the underlying HTS token system.

Impact:

A functional discrepancy can be observed between the HLiquity implementation and its Liquity equivalent whereby an extra authorization is required for staking operations.

Example:

packages/contracts/contracts/HLQTY/HLQTYToken.sol
176function sendToHLQTYStaking(address _sender, uint256 _amount) external override {
177 _requireCallerIsHLQTYStaking();
178 if (_isFirstYear()) { _requireSenderIsNotMultisig(_sender); } // Prevent the multisig from staking HLQTY
179 _transfer(_sender, hlqtyStakingAddress, _amount);
180}

Recommendation:

We advise this trait to be evaluated, and different balance transfer mechanisms to be utilized such as burn and mint operations that would not require authorization.

Alleviation (04618e407bddce5b22e9cadd787fd3334bd3afe6):

The Swisscoast team has opted to retain the current behaviour of the code in place as they do not consider the surplus approvals required to be disruptive for users or system contracts.

We would like to clarify that the burn / mint capabilities inferred by the exhibit's recommendation allude to the native Hedera token's burn and mint capabilities rather than the token implementation's.

HLQ-06M: Inexistent Acceptance of Native Funds

Description:

The HLQTYToken will set itself as responsible for auto-renewing its rent, however, it is not capable of accepting native funds (HBAR) to fund the rent during its auto renewal.

Impact:

When rent is enabled for smart contracts on the Hedera blockchain, the HLQTYToken will be unable to pay its rent and thus may become disabled unless special mechanisms are used to transfer funds to the contract directly.

Example:

packages/contracts/contracts/HLQTY/HLQTYToken.sol
84constructor
85(
86 address _communityIssuanceAddress,
87 address _hlqtyStakingAddress,
88 address _lockupFactoryAddress,
89 address _multisigAddress
90)
91 payable public
92{
93 checkContract(_communityIssuanceAddress);
94 checkContract(_hlqtyStakingAddress);
95 checkContract(_lockupFactoryAddress);
96
97 multisigAddress = _multisigAddress;
98 deploymentStartTime = block.timestamp;
99
100 communityIssuanceAddress = _communityIssuanceAddress;
101 hlqtyStakingAddress = _hlqtyStakingAddress;
102 lockupContractFactory = ILockupContractFactory(_lockupFactoryAddress);
103
104 // --- Deploy Hedera HTS ---
105
106 IHederaTokenService.HederaToken memory token;
107 token.name = _NAME;
108 token.symbol = _SYMBOL;
109 token.treasury = address(this);
110
111 token.expiry = createAutoRenewExpiry(address(this), 8000000);
112
113 IHederaTokenService.TokenKey[] memory keys = new IHederaTokenService.TokenKey[](1);
114 keys[0] = getSingleKey(KeyType.SUPPLY, KeyValueType.INHERIT_ACCOUNT_KEY, bytes(""));
115
116 token.tokenKeys = keys;
117
118 (int responseCode, address createdTokenAddress) =
119 HederaTokenService.createFungibleToken(token, 0, _DECIMALS);
120
121 _checkResponse(responseCode);
122 tokenAddress = createdTokenAddress;
123
124 uint _lpRewardsEntitlement = _1_MILLION.mul(4).div(3); // Allocate 1.33 million for LP rewards
125 lpRewardsEntitlement = _lpRewardsEntitlement;
126}

Recommendation:

We advise the contract to have an HLQTYToken::receive function introduced, permitting it to be funded with the necessary funds to auto-renew its rent.

Alleviation (04618e407bddce5b22e9cadd787fd3334bd3afe6):

The Swisscoast team clarified that the account responsible for paying the contract's rent is distinct from its contract instance and attached to the same account ID.

It is possible to fund the account directly without executing code on its contract counterpart effectively funding rent costs without a receive function in the contract involved.

We consider the exhibit invalid in light of this information and thus have marked it as nullified.