Skip to content

Latest commit

 

History

History
684 lines (441 loc) · 18.9 KB

report.md

File metadata and controls

684 lines (441 loc) · 18.9 KB

Aderyn Analysis Report

This report was generated by Aderyn, a static analysis tool built by Cyfrin, a blockchain security company. This report is not a substitute for manual audit or security review. It should not be relied upon for any purpose other than to assist in the identification of potential security vulnerabilities.

Table of Contents

Summary

Files Summary

Key Value
.sol Files 7
Total nSLOC 822

Files Details

Filepath nSLOC
src/CheckBalance.sol 127
src/Receiver.sol 250
src/Relayer.sol 82
src/Sender.sol 85
src/Subscription.sol 247
src/interfaces/ILogAutomation.sol 18
src/library/ReceiverSignedMessage.sol 13
Total 822

Issue Summary

Category No. of Issues
High 4
Low 15

High Issues

H-1: Arbitrary from passed to transferFrom (or safeTransferFrom)

Passing an arbitrary from address to transferFrom (or safeTransferFrom) can lead to loss of funds, because anyone can transfer tokens from the from address if an approval is made.

1 Found Instances
  • Found in src/Receiver.sol Line: 247

            IERC20(signedMessage.token).safeTransferFrom(

H-2: Functions send eth away from contract but performs no checks on any address.

Consider introducing checks for msg.sender to ensure the recipient of the money is as intended.

1 Found Instances
  • Found in src/Subscription.sol Line: 264

        function withdraw() external onlyOwner {

H-3: Return value of the function call is not checked.

Function returns a value but it is ignored.

5 Found Instances
  • Found in src/Receiver.sol Line: 254

            transferTokensPayLINK(
  • Found in src/Relayer.sol Line: 81

            sendMessage(i_subscriptionAddress, performData);
  • Found in src/Subscription.sol Line: 318

                checkBalance.sendRequest(true, AMOY_SUBSCRIPTION_ID, args);
  • Found in src/Subscription.sol Line: 321

                checkBalance.sendRequest(false, AMOY_SUBSCRIPTION_ID, args);
  • Found in src/Subscription.sol Line: 329

            s_sender.sendMessage(optionalChain, i_receiver, signedMessage);

H-4: Contract locks Ether without a withdraw function.

It appears that the contract includes a payable function to accept Ether but lacks a corresponding function to withdraw it, which leads to the Ether being locked in the contract. To resolve this issue, please implement a public or external function that allows for the withdrawal of Ether from the contract.

1 Found Instances
  • Found in src/Receiver.sol Line: 18

    contract Receiver is CCIPReceiver, EIP712, OwnerIsCreator {

Low Issues

L-1: Centralization Risk for trusted owners

Contracts have owners with privileged rights to perform admin tasks and need to be trusted to not perform malicious updates or drain funds.

10 Found Instances
  • Found in src/CheckBalance.sol Line: 13

    contract CheckBalance is FunctionsClient, Ownable {
  • Found in src/CheckBalance.sol Line: 107

        ) external onlyOwner initializedOnlyOnce {
  • Found in src/CheckBalance.sol Line: 120

        ) external onlyOwner hasInitialized returns (bytes32 requestId) {
  • Found in src/Receiver.sol Line: 134

        ) public onlyOwner {
  • Found in src/Sender.sol Line: 13

    contract Sender is Ownable {
  • Found in src/Sender.sol Line: 85

        ) external onlyOwner initializedOnlyOnce {
  • Found in src/Sender.sol Line: 99

        ) external onlyOwner hasInitialized returns (bytes32 messageId) {
  • Found in src/Subscription.sol Line: 20

    contract Subscription is ILogAutomation, CCIPReceiver, Ownable {
  • Found in src/Subscription.sol Line: 264

        function withdraw() external onlyOwner {
  • Found in src/Subscription.sol Line: 273

        function withdrawToken(address token) external onlyOwner {

L-2: Unsafe ERC20 Operations should not be used

ERC20 functions may not behave as expected. For example: return values are not always meaningful. It is recommended to use OpenZeppelin's SafeERC20 library.

5 Found Instances
  • Found in src/Receiver.sol Line: 176

            s_linkToken.approve(address(s_router), fees);
  • Found in src/Receiver.sol Line: 179

            IERC20(_token).approve(address(s_router), _amount);
  • Found in src/Relayer.sol Line: 121

            s_linkToken.approve(address(s_router), fees);
  • Found in src/Sender.sol Line: 132

            s_linkToken.approve(address(s_router), fees);
  • Found in src/Subscription.sol Line: 242

                token.approve(address(this), SUBSCRIPTION_FEE);

L-3: Missing checks for address(0) when assigning values to address state variables

Check for address(0) when assigning values to address state variables.

11 Found Instances
  • Found in src/Receiver.sol Line: 110

            s_router = IRouterClient(_router);
  • Found in src/Receiver.sol Line: 111

            s_linkToken = IERC20(_link);
  • Found in src/Relayer.sol Line: 55

            s_router = IRouterClient(_router);
  • Found in src/Relayer.sol Line: 56

            s_linkToken = LinkTokenInterface(_link);
  • Found in src/Sender.sol Line: 74

            s_router = IRouterClient(_router);
  • Found in src/Sender.sol Line: 75

            s_linkToken = LinkTokenInterface(_link);
  • Found in src/Subscription.sol Line: 88

            s_allowedToken = _allowedToken;
  • Found in src/Subscription.sol Line: 89

            s_allowedTokenForOptionalChain = _allowedTokenForOptionalChain;
  • Found in src/Subscription.sol Line: 90

            s_router = IRouterClient(_router);
  • Found in src/Subscription.sol Line: 91

            s_sepoliaCheckBalanceAddress = _sepoliaCheckBalanceAddress;
  • Found in src/Subscription.sol Line: 92

            s_sender = Sender(_sender);

L-4: public functions not used internally could be marked external

Instead of marking a function as public, consider marking it as external if it is not used internally.

3 Found Instances
  • Found in src/Receiver.sol Line: 131

        function withdrawToken(
  • Found in src/Subscription.sol Line: 161

        function paySubscriptionFeeForOptionalChain(
  • Found in src/Subscription.sol Line: 203

        function paySubscriptionFeeForPrimaryChain(

L-5: Define and use constant variables instead of using literals

If the same constant literal value is used multiple times, create a constant state variable and reference it throughout the contract.

2 Found Instances
  • Found in src/CheckBalance.sol Line: 170

                if (c >= 48 && c <= 57) {
  • Found in src/CheckBalance.sol Line: 171

                    result = result * 10 + (c - 48);

L-6: Event is missing indexed fields

Index event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields). Each event should use three indexed fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed.

7 Found Instances
  • Found in src/CheckBalance.sol Line: 64

        event Response(
  • Found in src/Receiver.sol Line: 76

        event TokensTransferred(
  • Found in src/Receiver.sol Line: 86

        event NonceUpdated(address indexed subscriber, uint256 nonce);
  • Found in src/Relayer.sol Line: 38

        event MessageSent(
  • Found in src/Sender.sol Line: 39

        event MessageSent(
  • Found in src/Subscription.sol Line: 55

        event SubscriberToSubscriptionUpdated(
  • Found in src/Subscription.sol Line: 62

        event Withdrawn(address token, uint256 amount);

L-7: Modifiers invoked only once can be shoe-horned into the function

5 Found Instances
  • Found in src/CheckBalance.sol Line: 75

        modifier initializedOnlyOnce() {
  • Found in src/CheckBalance.sol Line: 82

        modifier hasInitialized() {
  • Found in src/Receiver.sol Line: 94

        modifier validateReceiver(address _receiver) {
  • Found in src/Sender.sol Line: 52

        modifier initializedOnlyOnce() {
  • Found in src/Sender.sol Line: 59

        modifier hasInitialized() {

L-8: Large literal values multiples of 10000 can be replaced with scientific notation

Use e notation, for example: 1e18, instead of its full numeric value.

3 Found Instances
  • Found in src/CheckBalance.sol Line: 23

        uint32 private constant GASLIMIT = 300000;
  • Found in src/Relayer.sol Line: 103

                        gasLimit: 800_000, // Gas limit for the callback on the destination chain
  • Found in src/Sender.sol Line: 111

                        gasLimit: 3000_000, // Gas limit for the callback on the destination chain

L-9: Internal functions called only once can be inlined

Instead of separating the logic into a separate function, consider inlining the logic into the calling function. This can reduce the number of function calls and improve readability.

2 Found Instances
  • Found in src/Receiver.sol Line: 148

        function transferTokensPayLINK(
  • Found in src/Relayer.sol Line: 88

        function sendMessage(

L-10: Unused Custom Error

it is recommended that the definition be removed when custom error is unused

2 Found Instances
  • Found in src/Receiver.sol Line: 56

        error AmoyReceiver__FailedToWithdrawEth(
  • Found in src/Subscription.sol Line: 48

        error Subscription__AddConsumerFailed();

L-11: Loop condition contains state_variable.length that could be cached outside.

Cache the lengths of storage arrays if they are used and not modified in for loops.

1 Found Instances
  • Found in src/Subscription.sol Line: 287

            for (uint256 i = 0; i < s_subscriptionChainsSelector.length; i++) {

L-12: Unused Imports

Redundant import statement. Consider removing it.

1 Found Instances
  • Found in src/Subscription.sol Line: 11

    import {Client} from "@chainlink/contracts/src/v0.8/ccip/libraries/Client.sol";

L-13: State variable could be declared constant

State variables that are not updated following deployment should be declared constant to save gas. Add the constant attribute to state variables that never change.

2 Found Instances
  • Found in src/CheckBalance.sol Line: 25

        string private s_sourceForAllowedToken =
  • Found in src/CheckBalance.sol Line: 38

        string private s_sourceForNativeToken =

L-14: State variable changes but no event is emitted.

State variable changes in this function but no event is emitted.

3 Found Instances
  • Found in src/CheckBalance.sol Line: 105

        function setSubscriptionAsOwner(
  • Found in src/CheckBalance.sol Line: 116

        function sendRequest(
  • Found in src/Sender.sol Line: 83

        function setSubscriptionAsOwner(

L-15: State variable could be declared immutable

State variables that are should be declared immutable to save gas. Add the immutable attribute to state variables that are only changed in the constructor

13 Found Instances
  • Found in src/CheckBalance.sol Line: 24

        bytes32 private s_donID;
  • Found in src/Receiver.sol Line: 33

        IRouterClient private s_router;
  • Found in src/Receiver.sol Line: 34

        IERC20 private s_linkToken;
  • Found in src/Relayer.sol Line: 19

        IRouterClient private s_router;
  • Found in src/Relayer.sol Line: 20

        LinkTokenInterface private s_linkToken;
  • Found in src/Sender.sol Line: 18

        IRouterClient private s_router;
  • Found in src/Sender.sol Line: 19

        LinkTokenInterface private s_linkToken;
  • Found in src/Subscription.sol Line: 31

        address private s_sepoliaCheckBalanceAddress;
  • Found in src/Subscription.sol Line: 32

        uint64[] private s_subscriptionChainsSelector;
  • Found in src/Subscription.sol Line: 33

        address private s_allowedToken;
  • Found in src/Subscription.sol Line: 34

        address private s_allowedTokenForOptionalChain;
  • Found in src/Subscription.sol Line: 35

        IRouterClient private s_router;
  • Found in src/Subscription.sol Line: 38

        Sender private s_sender;