Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/snap/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ EXPLORER_TESTNET_BASE_URL=https://stellar.expert/explorer/testnet
TOKEN_API_BASE_URL=http://tokens.api.cx.metamask.io

# Static API Base URL
STATIC_API_BASE_URL=https://static.api.cx.metamask.io
STATIC_API_BASE_URL=https://static.cx.metamask.io

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

previous url is incorrect


# Price API Base URL
PRICE_API_BASE_URL=https://price.api.cx.metamask.io
Expand Down
8 changes: 7 additions & 1 deletion packages/snap/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
"message": "{reason}. Only continue if you trust every address involved."
},
"confirmation.simulationErrorTitle": {
"message": "This transaction was reverted during simulation."
"message": "This transaction is expected to fail."
},
"confirmation.simulationErrorSubtitle": {
"message": "{reason}"
Expand Down Expand Up @@ -454,6 +454,12 @@
"transactionScan.errors.insufficientFunds": {
"message": "Insufficient funds"
},
"transactionScan.errors.noTrustline": {
"message": "Trustline not found"
},
"transactionScan.errors.transactionExpired": {
"message": "Transaction expired"
},
"transactionScan.errors.invalidAddress": {
"message": "Invalid address"
},
Expand Down
8 changes: 7 additions & 1 deletion packages/snap/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
"message": "{reason}. Only continue if you trust every address involved."
},
"confirmation.simulationErrorTitle": {
"message": "This transaction was reverted during simulation."
"message": "This transaction is expected to fail."
},
"confirmation.simulationErrorSubtitle": {
"message": "{reason}"
Expand Down Expand Up @@ -454,6 +454,12 @@
"transactionScan.errors.insufficientFunds": {
"message": "Insufficient funds"
},
"transactionScan.errors.noTrustline": {
"message": "Trustline not found"
},
"transactionScan.errors.transactionExpired": {
"message": "Transaction expired"
},
"transactionScan.errors.invalidAddress": {
"message": "Invalid address"
},
Expand Down
8 changes: 7 additions & 1 deletion packages/snap/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@
"message": "{reason}. Only continue if you trust every address involved."
},
"confirmation.simulationErrorTitle": {
"message": "This transaction was reverted during simulation."
"message": "This transaction is expected to fail."
},
"confirmation.simulationErrorSubtitle": {
"message": "{reason}"
Expand Down Expand Up @@ -452,6 +452,12 @@
"transactionScan.errors.insufficientFunds": {
"message": "Insufficient funds"
},
"transactionScan.errors.noTrustline": {
"message": "Trustline not found"
},
"transactionScan.errors.transactionExpired": {
"message": "Transaction expired"
},
"transactionScan.errors.invalidAddress": {
"message": "Invalid address"
},
Expand Down
1 change: 0 additions & 1 deletion packages/snap/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ const accountResolver = new AccountResolver({
const signTransactionHandler = new SignTransactionHandler({
logger,
accountResolver,
transactionService,
confirmationUIController,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ describe('ConfirmSendHandler', () => {
assets: [
{
type: AssetChangeDirection.Out,
value: 1,
value: '1',
price: null,
symbol: assetMetadata.symbol,
name: assetMetadata.name,
Expand Down
2 changes: 1 addition & 1 deletion packages/snap/src/handlers/clientRequest/confirmSend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ export class ConfirmSendHandler extends BaseClientRequestHandler<
assets: [
{
type: AssetChangeDirection.Out,
value: Number(amount),
value: amount,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we use string instead of number to display

price: null,
symbol,
name: name ?? symbol,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('ConfirmationScanRefresher', () => {
assets: [
{
type: AssetChangeDirection.Out,
value: 2,
value: '2',
price: null,
symbol: 'USDC',
name: 'USD Coin',
Expand Down Expand Up @@ -136,7 +136,7 @@ describe('ConfirmationScanRefresher', () => {
assets: [
{
type: AssetChangeDirection.Out,
value: 1,
value: '1',
price: null,
symbol: 'XLM',
name: 'Stellar Lumens',
Expand Down Expand Up @@ -171,7 +171,7 @@ describe('ConfirmationScanRefresher', () => {
assets: [
{
type: AssetChangeDirection.Out,
value: 12.5,
value: '12.5',
price: null,
symbol: 'XLM',
name: 'Stellar Lumens',
Expand Down Expand Up @@ -216,7 +216,7 @@ describe('ConfirmationScanRefresher', () => {
assets: [
{
type: AssetChangeDirection.Out,
value: 7,
value: '7',
price: null,
symbol: 'XLM',
name: 'Stellar Lumens',
Expand Down Expand Up @@ -255,7 +255,7 @@ describe('ConfirmationScanRefresher', () => {
assets: [
{
type: AssetChangeDirection.Out,
value: 1,
value: '1',
price: null,
symbol: 'XLM',
name: 'Stellar Lumens',
Expand Down
46 changes: 2 additions & 44 deletions packages/snap/src/handlers/keyring/signTransaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,10 @@ import { SignTransactionHandler } from './signTransaction';
import { KnownCaip2ChainId } from '../../api';
import { AccountService } from '../../services/account';
import { generateStellarKeyringAccount } from '../../services/account/__mocks__/account.fixtures';
import { SimulationException } from '../../services/network/exceptions';
import { mockOnChainAccountService } from '../../services/on-chain-account/__mocks__/onChainAccount.fixtures';
import type { Transaction } from '../../services/transaction';
import {
TransactionService,
Transaction as WrappedTransaction,
} from '../../services/transaction';
import {
buildMockClassicTransaction,
createMockTransactionService,
} from '../../services/transaction/__mocks__/transaction.fixtures';
import { Transaction as WrappedTransaction } from '../../services/transaction';
import { buildMockClassicTransaction } from '../../services/transaction/__mocks__/transaction.fixtures';
import { WalletService } from '../../services/wallet';
import { getTestWallet } from '../../services/wallet/__mocks__/wallet.fixtures';
import type { ConfirmationUXController } from '../../ui/confirmation/controller';
Expand All @@ -41,7 +34,6 @@ describe('SignTransactionHandler', () => {
0,
);

const { transactionService } = createMockTransactionService();
const { accountService, onChainAccountService, walletService } =
mockOnChainAccountService();
const accountResolver = new AccountResolver({
Expand All @@ -58,11 +50,6 @@ describe('SignTransactionHandler', () => {
.spyOn(WalletService.prototype, 'resolveWallet')
.mockResolvedValue(wallet);

// Default: pass-through fee (no Soroban simulation needed for classic tx).
jest
.spyOn(TransactionService.prototype, 'computingFee')
.mockImplementation(async (tx) => tx);

const renderConfirmationDialog = jest.fn();
const confirmationUIController = {
renderConfirmationDialog,
Expand All @@ -74,15 +61,13 @@ describe('SignTransactionHandler', () => {
const handler = new SignTransactionHandler({
logger,
accountResolver,
transactionService,
confirmationUIController,
});

return {
handler,
mockAccount,
wallet,
transactionService,
renderConfirmationDialog,
};
}
Expand Down Expand Up @@ -271,31 +256,4 @@ describe('SignTransactionHandler', () => {
});
expect(renderConfirmationDialog).not.toHaveBeenCalled();
});

it('returns error -2 when fee simulation fails', async () => {
const {
handler,
mockAccount,
wallet,
transactionService,
renderConfirmationDialog,
} = setupHandler();

const transaction = buildMainnetPaymentFromWallet(wallet.address);
jest
.spyOn(transactionService, 'computingFee')
.mockRejectedValueOnce(new SimulationException('contract not found'));

const result = await handler.handle(
buildRequest(mockAccount.id, transaction.getRaw().toXDR()),
);

expect(result).toMatchObject({
error: {
code: Sep43ErrorCode.ExternalService,
ext: [expect.stringContaining('Failed to simulate transaction')],
},
});
expect(renderConfirmationDialog).not.toHaveBeenCalled();
});
});
18 changes: 5 additions & 13 deletions packages/snap/src/handlers/keyring/signTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import type { AccountResolver } from '../accountResolver';
import { BaseSep43KeyringHandler } from './base';
import type { Sep43Error } from './exceptions';
import type { StellarKeyringAccount } from '../../services/account';
import type { TransactionService } from '../../services/transaction';
import { OperationMapper, Transaction } from '../../services/transaction';
import {
assertTransactionScope,
Expand All @@ -35,19 +34,15 @@ export class SignTransactionHandler extends BaseSep43KeyringHandler<
SignTransactionRequest,
SignTransactionResponse
> {
readonly #transactionService: TransactionService;

readonly #confirmationUIController: ConfirmationUXController;

constructor({
logger,
accountResolver,
transactionService,
confirmationUIController,
}: {
logger: ILogger;
accountResolver: AccountResolver;
transactionService: TransactionService;
confirmationUIController: ConfirmationUXController;
}) {
super({
Expand All @@ -57,7 +52,6 @@ export class SignTransactionHandler extends BaseSep43KeyringHandler<
requestStruct: SignTransactionRequestStruct,
responseStruct: SignTransactionResponseStruct,
});
this.#transactionService = transactionService;
this.#confirmationUIController = confirmationUIController;
}

Expand All @@ -77,16 +71,14 @@ export class SignTransactionHandler extends BaseSep43KeyringHandler<
// verify the transaction scope matches the requested scope
assertTransactionScope(transaction, scope);

// Computing fee will inject the fee into the transaction
const transactionWithFee =
await this.#transactionService.computingFee(transaction);

if (!(await this.#confirmation(request, transactionWithFee, account))) {
// We do not process RPC simulation here, we trust the fee that provided by the dapp.
// If the transaction is invalid, the security scan will output the error.
if (!(await this.#confirmation(request, transaction, account))) {
Comment on lines +74 to +76

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is expected, we trust the dapp

throw new UserRejectedRequestError() as unknown as Error;
}

wallet.signTransaction(transactionWithFee);
const signedTxXdr = transactionWithFee.getRaw().toXDR();
wallet.signTransaction(transaction);
const signedTxXdr = transaction.getRaw().toXDR();

return {
signedTxXdr,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import type {
ScanTransactionRequest,
SecurityAlertsMetadata,
StellarTransactionScanRequest,
SecurityAlertsApiRequest,
StellarTransactionScanResponse,
} from './api';
import { TransactionScanException } from './exceptions';
Expand All @@ -27,7 +27,7 @@ import {

const SCOPE_TO_SECURITY_ALERTS_CHAIN: Record<
KnownCaip2ChainId,
StellarTransactionScanRequest['chain']
SecurityAlertsApiRequest['chain']
> = {
[KnownCaip2ChainId.Mainnet]: 'pubnet',
[KnownCaip2ChainId.Testnet]: 'testnet',
Expand Down Expand Up @@ -72,7 +72,7 @@ export class SecurityAlertsApiClient {
path: '/stellar/transaction/scan',
});

const requestBody: StellarTransactionScanRequest = {
const requestBody: SecurityAlertsApiRequest = {
account_address: accountAddress,
chain: SCOPE_TO_SECURITY_ALERTS_CHAIN[scope],
metadata: this.#getMetadata(origin),
Expand Down
Loading
Loading