Skip to content

Commit

Permalink
Merge branch 'feat/snap-fee-token-selection' into feat/sf-715-jsx-sup…
Browse files Browse the repository at this point in the history
…port-detection
  • Loading branch information
khanti42 authored Nov 19, 2024
2 parents d3c13f2 + ef8076e commit bd56316
Show file tree
Hide file tree
Showing 25 changed files with 618 additions and 512 deletions.
4 changes: 3 additions & 1 deletion packages/starknet-snap/src/__tests__/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,10 @@ export function generateTransactionRequests({
id: uuidv4(),
interfaceId: uuidv4(),
type: TransactionType.INVOKE,
networkName: 'Sepolia',
signer: address,
maxFee: '100',
feeToken:
selectedFeeToken:
feeTokens[Math.floor(generateRandomValue() * feeTokens.length)].symbol,
calls: [
{
Expand All @@ -339,6 +340,7 @@ export function generateTransactionRequests({
entrypoint: 'transfer',
},
],
includeDeploy: false,
});
}

Expand Down
14 changes: 14 additions & 0 deletions packages/starknet-snap/src/rpcs/__tests__/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ export function prepareConfirmDialog() {
};
}

/**
*
*/
export function prepareConfirmDialogInteractiveUI() {
const confirmDialogSpy = jest.spyOn(
snapHelper,
'createInteractiveConfirmDialog',
);
confirmDialogSpy.mockResolvedValue(true);
return {
confirmDialogSpy,
};
}

/**
*
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/starknet-snap/src/rpcs/execute-txn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { executeTxn as executeTxnUtil } from '../utils/starknetUtils';
import {
generateRandomFee,
mockAccount,
prepareConfirmDialog,
prepareConfirmDialogInteractiveUI,
prepareMockAccount,
} from './__tests__/helper';
import type { ExecuteTxnParams } from './execute-txn';
Expand All @@ -35,7 +35,7 @@ const prepareMockExecuteTxn = async (
networks: [STARKNET_SEPOLIA_TESTNET_NETWORK],
transactions: [],
};
const { confirmDialogSpy } = prepareConfirmDialog();
const { confirmDialogSpy } = prepareConfirmDialogInteractiveUI();

const account = await mockAccount(constants.StarknetChainId.SN_SEPOLIA);
prepareMockAccount(account, state);
Expand Down
172 changes: 39 additions & 133 deletions packages/starknet-snap/src/rpcs/execute-txn.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,29 @@
import type { Component, Json } from '@metamask/snaps-sdk';
import convert from 'ethereum-unit-converter';
import { type Json } from '@metamask/snaps-sdk';
import type { Call, Calldata } from 'starknet';
import { constants, TransactionStatus, TransactionType } from 'starknet';
import type { Infer } from 'superstruct';
import { object, string, assign, optional, any } from 'superstruct';
import { v4 as uuidv4 } from 'uuid';

import { AccountStateManager } from '../state/account-state-manager';
import { TokenStateManager } from '../state/token-state-manager';
import { TransactionStateManager } from '../state/transaction-state-manager';
import { FeeToken } from '../types/snapApi';
import type { TransactionRequest } from '../types/snapState';
import { VoyagerTransactionType, type Transaction } from '../types/snapState';
import { generateExecuteTxnFlow } from '../ui/utils';
import type { AccountRpcControllerOptions } from '../utils';
import {
AddressStruct,
BaseRequestStruct,
AccountRpcController,
confirmDialog,
UniversalDetailsStruct,
CallsStruct,
mapDeprecatedParams,
addressUI,
signerUI,
networkUI,
jsonDataUI,
dividerUI,
headerUI,
rowUI,
createInteractiveConfirmDialog,
callToTransactionReqCall,
} from '../utils';
import { UserRejectedOpError } from '../utils/exceptions';
import { logger } from '../utils/logger';
import {
createAccount,
executeTxn as executeTxnUtil,
Expand Down Expand Up @@ -112,6 +107,7 @@ export class ExecuteTxnRpc extends AccountRpcController<
): Promise<ExecuteTxnResponse> {
const { address, calls, abis, details } = params;
const { privateKey, publicKey } = this.account;
const callsArray = Array.isArray(calls) ? calls : [calls];

const { includeDeploy, suggestedMaxFee, estimateResults } =
await getEstimatedFees(
Expand All @@ -132,15 +128,38 @@ export class ExecuteTxnRpc extends AccountRpcController<
const version =
details?.version as unknown as constants.TRANSACTION_VERSION;

if (
!(await this.getExecuteTxnConsensus(
address,
accountDeployed,
calls,
suggestedMaxFee,
version,
))
) {
const formattedCalls = await Promise.all(
callsArray.map(async (call) =>
callToTransactionReqCall(
call,
this.network.chainId,
address,
this.tokenStateManager,
),
),
);

const request: TransactionRequest = {
chainId: this.network.chainId,
networkName: this.network.name,
id: uuidv4(),
interfaceId: '',
type: TransactionType.INVOKE,
signer: address,
maxFee: suggestedMaxFee,
calls: formattedCalls,
selectedFeeToken:
version === constants.TRANSACTION_VERSION.V3
? FeeToken.STRK
: FeeToken.ETH,
includeDeploy,
};

const interfaceId = await generateExecuteTxnFlow(request);

request.interfaceId = interfaceId;

if (!(await createInteractiveConfirmDialog(interfaceId))) {
throw new UserRejectedOpError() as unknown as Error;
}

Expand Down Expand Up @@ -213,119 +232,6 @@ export class ExecuteTxnRpc extends AccountRpcController<
});
}

protected async getExecuteTxnConsensus(
address: string,
accountDeployed: boolean,
calls: Call[] | Call,
maxFee: string,
version?: constants.TRANSACTION_VERSION,
) {
const { name: chainName, chainId } = this.network;
const callsArray = Array.isArray(calls) ? calls : [calls];

const components: Component[] = [];
const feeToken: FeeToken =
version === constants.TRANSACTION_VERSION.V3
? FeeToken.STRK
: FeeToken.ETH;

components.push(headerUI('Do you want to sign this transaction?'));
components.push(
signerUI({
address,
chainId,
}),
);

// Display a message to indicate the signed transaction will include an account deployment
if (!accountDeployed) {
components.push(headerUI(`The account will be deployed`));
}

components.push(dividerUI());
components.push(
rowUI({
label: `Estimated Gas Fee (${feeToken})`,
value: convert(maxFee, 'wei', 'ether'),
}),
);

components.push(dividerUI());
components.push(
networkUI({
networkName: chainName,
}),
);

// Iterate over each call in the calls array
for (const call of callsArray) {
const { contractAddress, calldata, entrypoint } = call;
components.push(dividerUI());
components.push(
addressUI({
label: 'Contract',
address: contractAddress,
chainId,
}),
);

components.push(
jsonDataUI({
label: 'Call Data',
data: calldata,
}),
);

// If the contract is an ERC20 token and the function is 'transfer', display sender, recipient, and amount
const token = await this.tokenStateManager.getToken({
address: contractAddress,
chainId,
});

if (token && entrypoint === 'transfer' && calldata) {
try {
const senderAddress = address;
const recipientAddress = calldata[0]; // Assuming the first element in calldata is the recipient
let amount = '';

if ([3, 6, 9, 12, 15, 18].includes(token.decimals)) {
amount = convert(calldata[1], -1 * token.decimals, 'ether');
} else {
amount = (
Number(calldata[1]) * Math.pow(10, -1 * token.decimals)
).toFixed(token.decimals);
}
components.push(dividerUI());
components.push(
addressUI({
label: 'Sender Address',
address: senderAddress,
chainId,
}),
dividerUI(),
addressUI({
label: 'Recipient Address',
address: recipientAddress,
chainId,
}),
dividerUI(),
rowUI({
label: `Amount (${token.symbol})`,
value: amount,
}),
);
} catch (error) {
logger.warn(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`error found in amount conversion: ${error}`,
);
}
}
}

return await confirmDialog(components);
}

protected createDeployTxn(
address: string,
transactionHash: string,
Expand Down
Loading

0 comments on commit bd56316

Please sign in to comment.