• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

safe-global / safe-client-gateway / 18192499382

02 Oct 2025 12:06PM UTC coverage: 89.205% (+0.008%) from 89.197%
18192499382

Pull #2721

github

tmjssz
refactor: improve extraction of contract address
Pull Request #2721: refactor(safe-shield): Enhanced transaction decoding via TransactionsService

3781 of 4656 branches covered (81.21%)

Branch coverage included in aggregate %.

42 of 47 new or added lines in 5 files covered. (89.36%)

2 existing lines in 1 file now uncovered.

12697 of 13816 relevant lines covered (91.9%)

525.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.77
/src/modules/safe-shield/safe-shield.service.ts
1
import { Inject, Injectable } from '@nestjs/common';
2✔
2
import { RecipientAnalysisService } from './recipient-analysis/recipient-analysis.service';
2✔
3
import { ContractAnalysisService } from './contract-analysis/contract-analysis.service';
2✔
4
import { ThreatAnalysisService } from './threat-analysis/threat-analysis.service';
2✔
5
import { type Address, type Hex } from 'viem';
6
import type {
7
  ContractAnalysisResponse,
8
  RecipientAnalysisResponse,
9
} from './entities/analysis-responses.entity';
10
import type { DecodedTransactionData } from '@/modules/safe-shield/entities/transaction-data.entity';
11
import { ILoggingService, LoggingService } from '@/logging/logging.interface';
2✔
12
import { mapDecodedTransactions } from './utils/transaction-mapping.utils';
2✔
13
import { TransactionsService } from '@/routes/transactions/transactions.service';
2✔
14
import { Operation } from '@/domain/safe/entities/operation.entity';
2✔
15
import type { TransactionInfo } from '@/routes/transactions/entities/transaction-info.entity';
16

17
/**
18
 * Main orchestration service for Safe Shield transaction analysis.
19
 *
20
 * This service coordinates all analysis types (recipient, contract, threat)
21
 * and provides the main entry points for transaction safety checks.
22
 * It acts as a facade that delegates to specialized analysis services.
23
 */
24
@Injectable()
25
export class SafeShieldService {
2✔
26
  constructor(
27
    private readonly recipientAnalysisService: RecipientAnalysisService,
2✔
28
    private readonly contractAnalysisService: ContractAnalysisService,
2✔
29
    private readonly threatAnalysisService: ThreatAnalysisService,
2✔
30
    @Inject(LoggingService)
31
    private readonly loggingService: ILoggingService,
2✔
32
    private readonly transactionsService: TransactionsService,
2✔
33
  ) {}
34

35
  /**
36
   * Analyzes recipients in a transaction, including inner calls if it's a multiSend.
37
   *
38
   * @param args - Analysis parameters
39
   * @param args.chainId - The chain ID
40
   * @param args.safeAddress - The Safe address
41
   * @param args.tx - The transaction data
42
   * @param args.tx.to - The transaction recipient address
43
   * @param args.tx.data - The transaction data (optional)
44
   * @param args.tx.value - The transaction value (optional)
45
   * @param args.tx.operation - The transaction operation (optional)
46
   * @returns Map of recipient addresses to their analysis results
47
   */
48
  async analyzeRecipient({
49
    chainId,
50
    safeAddress,
51
    tx,
52
  }: {
53
    chainId: string;
54
    safeAddress: Address;
55
    tx: {
56
      to: Address;
57
      data?: Hex;
58
      value?: bigint | string;
59
      operation?: Operation;
60
    };
61
  }): Promise<RecipientAnalysisResponse> {
62
    const { transactions } = await this.decodeTransaction({
18✔
63
      chainId,
64
      safeAddress,
65
      tx,
66
    });
67

68
    if (transactions.length > 0) {
18✔
69
      return this.recipientAnalysisService.analyze({
16✔
70
        chainId,
71
        safeAddress,
72
        transactions,
73
      });
74
    }
75

76
    return {};
2✔
77
  }
78

79
  /**
80
   * Analyzes contracts in a transaction, including inner calls if it's a multiSend.
81
   *
82
   * @param args - Analysis parameters
83
   * @param args.chainId - The chain ID
84
   * @param args.safeAddress - The Safe address
85
   * @param args.tx - The transaction data
86
   * @param args.tx.to - The transaction contract address
87
   * @param args.tx.data - The transaction data
88
   * @param args.tx.value - The transaction value (optional)
89
   * @param args.tx.operation - The transaction operation (optional)
90
   * @returns Map of contract addresses to their analysis results
91
   */
92
  async analyzeContracts({
93
    chainId,
94
    safeAddress,
95
    tx,
96
  }: {
97
    chainId: string;
98
    safeAddress: Address;
99
    tx: {
100
      to: Address;
101
      data: Hex;
102
      value?: bigint | string;
103
      operation?: Operation;
104
    };
105
  }): Promise<ContractAnalysisResponse> {
NEW
106
    const { transactions } = await this.decodeTransaction({
×
107
      chainId,
108
      safeAddress,
109
      tx,
110
    });
111

NEW
112
    if (transactions.length > 0) {
×
NEW
113
      return this.contractAnalysisService.analyze({
×
114
        chainId,
115
        safeAddress,
116
        transactions,
117
      });
118
    }
119

NEW
120
    return {};
×
121
  }
122

123
  /**
124
   * Decodes a transaction.
125
   * @param args - The arguments for decoding the transaction.
126
   * @param args.chainId - The chain ID.
127
   * @param args.safeAddress - The Safe address.
128
   * @param args.tx - The transaction.
129
   * @returns The decoded transaction.
130
   */
131
  private async decodeTransaction({
132
    chainId,
133
    safeAddress,
134
    tx,
135
  }: {
136
    chainId: string;
137
    safeAddress: Address;
138
    tx: {
139
      to: Address;
140
      data?: Hex;
141
      value?: bigint | string;
142
      operation?: Operation;
143
    };
144
  }): Promise<{
145
    transactions: Array<DecodedTransactionData>;
146
    txInfo?: TransactionInfo;
147
  }> {
148
    const { data = '0x', value = '0', operation = Operation.CALL } = tx;
18!
149

150
    try {
18✔
151
      const txPreview = await this.transactionsService.previewTransaction({
18✔
152
        chainId,
153
        safeAddress,
154
        previewTransactionDto: {
155
          ...tx,
156
          data,
157
          operation,
158
          value: value.toString(),
159
        },
160
      });
161

162
      const decodedTransactionData: DecodedTransactionData = {
16✔
163
        ...tx,
164
        data,
165
        operation,
166
        value,
167
        dataDecoded: txPreview?.txData?.dataDecoded ?? null,
71!
168
      };
169

170
      return {
16✔
171
        transactions: mapDecodedTransactions(decodedTransactionData),
172
        txInfo: txPreview?.txInfo,
24!
173
      };
174
    } catch (error) {
175
      this.loggingService.warn(`Failed to decode transaction: ${error}`);
2✔
176
      return { transactions: [] };
2✔
177
    }
178
  }
179
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc