Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: perform eth_sendTransaction via EVM module #51

Merged
merged 10 commits into from
Oct 10, 2024
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
"sentry": "node sentryscript.js"
},
"dependencies": {
"@avalabs/avalanche-module": "0.7.0",
"@avalabs/avalanche-module": "0.0.0-feat-update-fee-config-20240926100305",
"@avalabs/avalanchejs": "4.0.5",
"@avalabs/bitcoin-module": "0.7.0",
"@avalabs/bitcoin-module": "0.0.0-feat-update-fee-config-20240926100305",
"@avalabs/bridge-unified": "2.1.0",
"@avalabs/core-bridge-sdk": "3.1.0-alpha.4",
"@avalabs/core-chains-sdk": "3.1.0-alpha.4",
Expand All @@ -37,11 +37,11 @@
"@avalabs/core-token-prices-sdk": "3.1.0-alpha.4",
"@avalabs/core-utils-sdk": "3.1.0-alpha.4",
"@avalabs/core-wallets-sdk": "3.1.0-alpha.4",
"@avalabs/evm-module": "0.7.0",
"@avalabs/evm-module": "0.0.0-feat-update-fee-config-20240926100305",
"@avalabs/glacier-sdk": "3.1.0-alpha.4",
"@avalabs/hw-app-avalanche": "0.14.1",
"@avalabs/types": "3.1.0-alpha.3",
"@avalabs/vm-module-types": "0.7.0",
"@avalabs/vm-module-types": "0.0.0-feat-update-fee-config-20240926100305",
"@blockaid/client": "0.10.0",
"@coinbase/cbpay-js": "1.6.0",
"@cubist-labs/cubesigner-sdk": "0.3.28",
Expand Down
1 change: 0 additions & 1 deletion src/background/connections/dAppConnection/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export enum DAppProviderRequest {
WALLET_GET_CHAIN = 'wallet_getEthereumChain',
WALLET_SWITCH_ETHEREUM_CHAIN = 'wallet_switchEthereumChain',
WALLET_WATCH_ASSET = 'wallet_watchAsset',
ETH_SEND_TX = 'eth_sendTransaction',
PERSONAL_EC_RECOVER = 'personal_ecRecover',
PERSONAL_SIGN = 'personal_sign',
ETH_SIGN_TYPED_DATA_V4 = 'eth_signTypedData_v4',
Expand Down
2 changes: 0 additions & 2 deletions src/background/connections/dAppConnection/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { WalletGetPermissionsHandler } from '@src/background/services/permission
import { WalletRequestPermissionsHandler } from '@src/background/services/permissions/handlers/wallet_requestPermissions';
import { WalletWatchAssetHandler } from '@src/background/services/settings/events/wallet_watchAsset';
import { WalletGetEthereumChainHandler } from '@src/background/services/network/handlers/wallet_getEthereumChain';
import { EthSendTransactionHandler } from '@src/background/services/wallet/handlers/eth_sendTransaction';
import { AvalancheSelectWalletHandler } from '@src/background/services/web3/handlers/avalanche_selectWallet';
import { ConnectRequestHandler } from '@src/background/services/web3/handlers/connect';
import { AvalancheGetProviderState } from '@src/background/services/web3/handlers/avalanche_getProviderState';
Expand Down Expand Up @@ -69,7 +68,6 @@ import { AvalancheRenameAccountHandler } from '@src/background/services/accounts
{ token: 'DAppRequestHandler', useToken: WalletGetPermissionsHandler },
{ token: 'DAppRequestHandler', useToken: WalletRequestPermissionsHandler },
{ token: 'DAppRequestHandler', useToken: WalletWatchAssetHandler },
{ token: 'DAppRequestHandler', useToken: EthSendTransactionHandler },
{ token: 'DAppRequestHandler', useToken: ConnectRequestHandler },
{ token: 'DAppRequestHandler', useToken: AvalancheGetProviderState },
{
Expand Down
1 change: 1 addition & 0 deletions src/background/connections/extensionConnection/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export enum ExtensionRequest {

ACTION_GET = 'action_getAction',
ACTION_UPDATE = 'action_updateAction',
ACTION_UPDATE_TX_DATA = 'action_updateTxData',

PERMISSIONS_ADD_DOMAIN = 'permissions_addDomain',
PERMISSIONS_GET_PERMISSIONS = 'permissions_getPermissionsForDomain',
Expand Down
2 changes: 2 additions & 0 deletions src/background/connections/extensionConnection/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ import { StartBalancesPollingHandler } from '@src/background/services/balances/h
import { StopBalancesPollingHandler } from '@src/background/services/balances/handlers/stopBalancesPolling';
import { BalancesUpdatedEvents } from '@src/background/services/balances/events/balancesUpdatedEvent';
import { UnifiedBridgeTrackTransfer } from '@src/background/services/unifiedBridge/handlers/unifiedBridgeTrackTransfer';
import { UpdateActionTxDataHandler } from '@src/background/services/actions/handlers/updateTxData';

/**
* TODO: GENERATE THIS FILE AS PART OF THE BUILD PROCESS
Expand All @@ -141,6 +142,7 @@ import { UnifiedBridgeTrackTransfer } from '@src/background/services/unifiedBrid
{ token: 'ExtensionRequestHandler', useToken: DeleteAccountHandler },
{ token: 'ExtensionRequestHandler', useToken: GetActionHandler },
{ token: 'ExtensionRequestHandler', useToken: UpdateActionHandler },
{ token: 'ExtensionRequestHandler', useToken: UpdateActionTxDataHandler },
{ token: 'ExtensionRequestHandler', useToken: ClearAnalyticsIdsHandler },
{ token: 'ExtensionRequestHandler', useToken: GetAnalyticsIdsHandler },
{ token: 'ExtensionRequestHandler', useToken: InitAnalyticsIdsHandler },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { NetworkService } from '@src/background/services/network/NetworkService';
import { ActiveNetworkMiddleware } from './ActiveNetworkMiddleware';
import { RpcMethod } from '@avalabs/vm-module-types';
import getTargetNetworkForTx from '@src/background/services/wallet/handlers/eth_sendTransaction/utils/getTargetNetworkForTx';

jest.mock(
'@src/background/services/wallet/handlers/eth_sendTransaction/utils/getTargetNetworkForTx'
);

describe('src/background/connections/middlewares/ActiveNetworkMiddleware', () => {
const networkService = {
getNetwork: jest.fn(),
} as unknown as NetworkService;

beforeEach(() => {
jest.resetAllMocks();
});

it('errors out for cross-environment EVM transaction attempts', async () => {
const call = ActiveNetworkMiddleware(networkService);

const next = jest.fn();
const onError = jest.fn();
const error = new Error('Cross-env error');

jest.mocked(getTargetNetworkForTx).mockRejectedValueOnce(error);

await call(
{
request: {
params: {
scope: 'eip155:43114', // C-Chain Mainnet
request: {
method: RpcMethod.ETH_SEND_TRANSACTION,
params: [
{
chainId: '0xa869', // C-Chain Fuji (43113)
},
],
},
},
},
} as any,
next,
onError
);

expect(next).not.toHaveBeenCalled();
expect(onError).toHaveBeenCalledWith(error);
});
});
36 changes: 28 additions & 8 deletions src/background/connections/middlewares/ActiveNetworkMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
ExtensionConnectionMessage,
ExtensionConnectionMessageResponse,
} from '../models';
import { RpcMethod } from '@avalabs/vm-module-types';
import getTargetNetworkForTx from '@src/background/services/wallet/handlers/eth_sendTransaction/utils/getTargetNetworkForTx';

export function ActiveNetworkMiddleware(
networkService: NetworkService
Expand All @@ -15,21 +17,39 @@ export function ActiveNetworkMiddleware(
JsonRpcResponse | ExtensionConnectionMessageResponse
> {
return async (context, next, error) => {
const { scope } = context.request.params;
const {
scope,
request: { method, params },
} = context.request.params;

if (scope) {
const network = await networkService.getNetwork(
context.request.params.scope
);
if (!scope) {
next();
return;
}

const isEthSendTx = method === RpcMethod.ETH_SEND_TRANSACTION;
const hasParams = Array.isArray(params) && typeof params[0] === 'object';

let network;

if (!network) {
error(new Error(`Unrecognized network: ${scope}`));
if (isEthSendTx && hasParams) {
try {
network = await getTargetNetworkForTx(params[0], networkService, scope);
} catch (err: any) {
error(err);
return;
}
} else {
network = await networkService.getNetwork(scope);
}

context.network = network;
if (!network) {
error(new Error(`Unrecognized network: ${scope}`));
return;
}

context.network = network;

next();
};
}
27 changes: 27 additions & 0 deletions src/background/services/actions/ActionsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ACTION_HANDLED_BY_MODULE } from '@src/background/models';
import { DAppProviderRequest } from '@src/background/connections/dAppConnection/models';
import { getUpdatedSigningData } from '@src/utils/actions/getUpdatedActionData';
import { ApprovalController } from '@src/background/vmModules/ApprovalController';
import { BtcTxUpdateFn, EvmTxUpdateFn } from '@avalabs/vm-module-types';

@singleton()
export class ActionsService implements OnStorageReady {
Expand Down Expand Up @@ -212,6 +213,32 @@ export class ActionsService implements OnStorageReady {
}
}

async updateTx(
gergelylovas marked this conversation as resolved.
Show resolved Hide resolved
id: string,
newData: Parameters<EvmTxUpdateFn>[0] | Parameters<BtcTxUpdateFn>[0]
) {
const currentPendingRequests = await this.getActions();
const pendingRequest = currentPendingRequests[id];

if (!pendingRequest) {
throw new Error(`No request found with id: ${id}`);
}

const { signingData, displayData } = this.approvalController.updateTx(
id,
newData
);

await this.saveActions({
...currentPendingRequests,
[id]: {
...pendingRequest,
signingData,
displayData,
},
});
}

addListener(
event: ActionsEvent.ACTION_COMPLETED,
callback: (data: {
Expand Down
60 changes: 60 additions & 0 deletions src/background/services/actions/handlers/updateTxData.ts
gergelylovas marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { injectable } from 'tsyringe';
import { EvmTxUpdateFn, BtcTxUpdateFn } from '@avalabs/vm-module-types';

import { SendErrorMessage } from '@src/utils/send/models';
import { ExtensionRequest } from '@src/background/connections/extensionConnection/models';
import { ExtensionRequestHandler } from '@src/background/connections/models';

import { ActionsService } from '../ActionsService';

type HandlerType = ExtensionRequestHandler<
ExtensionRequest.ACTION_UPDATE_TX_DATA,
null,
[
id: string,
newData: Parameters<EvmTxUpdateFn>[0] | Parameters<BtcTxUpdateFn>[0]
]
>;

@injectable()
export class UpdateActionTxDataHandler implements HandlerType {
method = ExtensionRequest.ACTION_UPDATE_TX_DATA as const;

constructor(private actionsService: ActionsService) {}
handle: HandlerType['handle'] = async ({ request }) => {
const [id, newData] = request.params;

if (!id) {
return {
...request,
error: 'no request id in params',
};
}

const actions = await this.actionsService.getActions();

if (!actions) {
return { ...request, error: 'no messages found' };
}

const action = actions[id];

if (!action) {
return { ...request, error: 'no message found with that id' };
}

try {
await this.actionsService.updateTx(id, newData);
return { ...request, result: null };
} catch (err: any) {
if (err?.message === 'Unable to create transaction') {
return {
...request,
error: SendErrorMessage.INSUFFICIENT_BALANCE_FOR_FEE,
};
}

return { ...request, error: err };
}
};
}
2 changes: 1 addition & 1 deletion src/background/services/actions/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export interface ActionUpdate<DisplayData = any> {
id: any;
status: ActionStatus;
displayData?: DisplayData;
signingData?: SigningData;
signingData?: never; // Don't allow overriding signingData this way
result?: any;
error?: string;
tabId?: number;
Expand Down
Loading
Loading