欢迎光临
我们一直在努力

区块链钱包开发(二十一)—— 一次交易的全流程分析

本文将详细分析一次转账交易在钱包中是如何处理的,包括“创建交易→展示确认→用户确认→发布上链→确认落链”的端到端全流程分析。

阶段 0:UI 发起“创建未批准交易”

  • 创建交易并跳转到确认页

// metamask-extension/ui/store/actions.ts
export function addTransactionAndRouteToConfirmationPage(
txParams, options?
): ThunkAction<Promise<TransactionMeta | null>, > {


return async (dispatch) => {


const actionId = generateActionId();
try {


const transactionMeta = await submitRequestToBackground<TransactionMeta>(
\’addTransaction\’,
[txParams, {

options, actionId, origin: ORIGIN_METAMASK }],
);
dispatch(showConfTxPage());
return transactionMeta;
} catch (error) {


dispatch(hideLoadingIndication());
dispatch(displayWarning(error));
throw error;
}
};
}

要点:

  • UI 生成 actionId,用于去重(后台会用它避免重复创建)。
  • origin: ORIGIN_METAMASK 标记来源。

阶段 1:后台 TransactionController.addTransaction

在后台,addTransaction 完成参数标准化、校验、构建 txMeta、推送“未批准交易事件”,并返回一个 result 对象,里面的 result 是一个 promise:等待审批完成后继续处理(参见下面的 #processApproval)。

// core/packages/transaction-controller/src/TransactionController.ts
async addTransaction(txParams, options): Promise<Result> {


// 1) 解构 options
// 2) 标准化 txParams
txParams = normalizeTransactionParams(txParams);

// 3) 校验网络、获取 chainId / ethQuery
const chainId = this.#getChainId(networkClientId);
const ethQuery = this.#getEthQuery({

networkClientId });

// 4) 校验 origin 权限、内部账户等
const permittedAddresses = origin === undefined ? undefined : await this.#getPermittedAccounts?.(origin);
const internalAccounts = this.#getInternalAccounts();
await validateTransactionOrigin({

});

// 5) 获取委托地址(可选)、EIP-1559 兼容性与基本 txParams 校验
const delegationAddressPromise = getDelegationAddress(txParams.from as Hex, ethQuery).catch(() => undefined);
const isEIP1559Compatible = await this.#getEIP1559Compatibility(networkClientId);
validateTxParams(txParams, isEIP1559Compatible, chainId);
if (!txParams.type) setEnvelopeType(txParams, isEIP1559Compatible);

// 6) 批次 ID 去重(非 MetaMask origin 不允许重复)
const isDuplicateBatchId =
if (isDuplicateBatchId && origin && origin !== ORIGIN_METAMASK) throw new JsonRpcError()

// 7) 生成 dApp 建议 gas、识别交易类型
const dappSuggestedGasFees = this.#generateDappSuggestedGasFees(txParams, origin);
const transactionType = type ?? (await determineTransactionType(txParams, ethQuery)).type;
const delegationAddress = await delegationAddressPromise;

// 8) 通过 actionId 去重:若已存在同 actionId 交易,复用;否则创建新的 txMeta
const existingTransactionMeta = this.#getTransactionWithActionId(actionId);
let addedTransactionMeta: TransactionMeta = existingTransactionMeta ? cloneDeep(existingTransactionMeta) : {


actionId, batchId, chainId, dappSuggestedGasFees, delegationAddress,
deviceConfirmedOn, disableGasBuffer, id: random(), isFirstTimeInteraction: undefined,
nestedTransactions, networkClientId, origin, securityAlertResponse,
status: TransactionStatus.unapproved as const,
time: Date.now(), txParams, type: transactionType,
userEditedGasLimit: false, verifiedOnBlockchain: false,
};

// 9) afterAdd hook(可能对 tx 进一步修正)
const {

updateTransaction } = await this.#afterAdd({

transactionMeta: addedTransactionMeta });
if (updateTransaction) {


addedTransactionMeta.txParamsOriginal = cloneDeep(addedTransactionMeta.txParams);
updateTransaction(addedTransactionMeta);
}

// 10) 估算 gas 属性(trace 包裹)
await this.#trace({

name: \’Estimate Gas Properties\’, parentContext: traceContext },
(context) => this.#updateGasProperties(addedTransactionMeta, {

traceContext: context }),
);

// 11) 首次创建(非重复)时,写安全扫描、历史快照、swaps 更新、添加元数据、模拟数据/首次交互标记
if (!existingTransactionMeta) {


if (method && this.#securityProviderRequest) {


const resp = await this.#securityProviderRequest(addedTransactionMeta, method);
addedTransactionMeta.securityProviderResponse = resp;
}
if (!this.#isSendFlowHistoryDisabled) {


addedTransactionMeta.sendFlowHistory = sendFlowHistory ?? [];
}
if (!this.#isHistoryDisabled) {


addedTransactionMeta = addInitialHistorySnapshot(addedTransactionMeta);
}
addedTransactionMeta = updateSwapsTransaction(addedTransactionMeta, transactionType, swaps, {

});
this.#addMetadata(addedTransactionMeta);

if (requireApproval !== false) {


this.#updateSimulationData(addedTransactionMeta, {

traceContext }).catch()
this.#updateFirstTimeInteraction(addedTransactionMeta, {

traceContext }).catch()
} else {


log(\’Skipping simulation & first interaction update as approval not required\’);
}

// 12) 发布“未批准交易已添加”事件,UI 看到它后进入确认页面
this.messagingSystem.publish(

赞(0)
未经允许不得转载:171主机测评 » 区块链钱包开发(二十一)—— 一次交易的全流程分析
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址