Skip to content

Commit

Permalink
Refactor and better document tx annotations
Browse files Browse the repository at this point in the history
There's no reason to ever skip returning a tx annotation — we can always
fall back to a generic contract interaction.

Refs #1234
  • Loading branch information
mhluongo committed Aug 7, 2022
1 parent f854c78 commit 6fbcc36
Showing 1 changed file with 35 additions and 30 deletions.
65 changes: 35 additions & 30 deletions background/services/enrichment/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ export default class EnrichmentService extends BaseService<Events> {
)
}

/**
* Resolve an annotation for a partial transaction request, or a pending
* or mined transaction.
*/
async resolveTransactionAnnotation(
network: EVMNetwork,
transaction:
Expand All @@ -118,17 +122,20 @@ export default class EnrichmentService extends BaseService<Events> {
blockHash?: string
}),
desiredDecimals: number
): Promise<TransactionAnnotation | undefined> {
let txAnnotation: TransactionAnnotation | undefined
): Promise<TransactionAnnotation> {
// By default, annotate all requests as contract interactions
let txAnnotation: TransactionAnnotation = {
blockTimestamp: undefined,
timestamp: Date.now(),
type: "contract-interaction",
}

const resolvedTime = Date.now()
let block: AnyEVMBlock | undefined

let hasInsufficientFunds = false

const { gasLimit, maxFeePerGas, maxPriorityFeePerGas, blockHash } =
transaction

// If this is a transaction request...
if (gasLimit && maxFeePerGas && maxPriorityFeePerGas) {
const gasFee = gasLimit * maxFeePerGas
const {
Expand All @@ -137,26 +144,37 @@ export default class EnrichmentService extends BaseService<Events> {
address: transaction.from,
network,
})
hasInsufficientFunds =
gasFee + (transaction.value ?? 0n) > baseAssetBalance
// ... and if the wallet doesn't have enough base asset to cover gas,
// push a warning
if (gasFee + (transaction.value ?? 0n) > baseAssetBalance) {
txAnnotation.warnings ??= []
txAnnotation.warnings.push("insufficient-funds")
}
}

// If the transaction has been mined, get the block and set the timestamp
if (blockHash) {
block = await this.chainService.getBlockData(network, blockHash)
txAnnotation = {
...txAnnotation,
blockTimestamp: block?.timestamp,
}
}

// If the tx is missing a recipient, its a contract deployment.
if (typeof transaction.to === "undefined") {
// A missing recipient means a contract deployment.
txAnnotation = {
timestamp: resolvedTime,
blockTimestamp: block?.timestamp,
...txAnnotation,
type: "contract-deployment",
}
} else if (
transaction.input === null ||
transaction.input === "0x" ||
typeof transaction.input === "undefined"
) {
// If the tx has no data, it's either a simple ETH send, or it's relying
// on a contract that's `payable` to execute code

const { name: toName } = (await this.nameService.lookUpName({
address: transaction.to,
network,
Expand All @@ -171,8 +189,7 @@ export default class EnrichmentService extends BaseService<Events> {
// over the 21k required to send ETH is a more complex contract interaction
if (typeof transaction.value !== "undefined") {
txAnnotation = {
timestamp: resolvedTime,
blockTimestamp: block?.timestamp,
...txAnnotation,
type: "asset-transfer",
senderAddress: transaction.from,
recipientName: toName,
Expand All @@ -188,9 +205,7 @@ export default class EnrichmentService extends BaseService<Events> {
} else {
// Fall back on a standard contract interaction.
txAnnotation = {
timestamp: resolvedTime,
blockTimestamp: block?.timestamp,
type: "contract-interaction",
...txAnnotation,
contractName: toName,
}
}
Expand Down Expand Up @@ -221,8 +236,7 @@ export default class EnrichmentService extends BaseService<Events> {

// We have an ERC-20 transfer
txAnnotation = {
timestamp: resolvedTime,
blockTimestamp: block?.timestamp,
...txAnnotation,
type: "asset-transfer",
transactionLogoURL,
senderAddress: erc20Tx.args.from ?? transaction.from,
Expand Down Expand Up @@ -251,8 +265,7 @@ export default class EnrichmentService extends BaseService<Events> {
})) ?? { name: undefined }

txAnnotation = {
timestamp: resolvedTime,
blockTimestamp: block?.timestamp,
...txAnnotation,
type: "asset-approval",
transactionLogoURL,
spenderAddress: erc20Tx.args.spender,
Expand All @@ -273,8 +286,7 @@ export default class EnrichmentService extends BaseService<Events> {

// Fall back on a standard contract interaction.
txAnnotation = {
timestamp: resolvedTime,
blockTimestamp: block?.timestamp,
...txAnnotation,
type: "contract-interaction",
// Include the logo URL if we resolve it even if the interaction is
// non-specific; the UI can choose to use it or not, but if we know the
Expand All @@ -291,7 +303,7 @@ export default class EnrichmentService extends BaseService<Events> {
transaction.logs,
network,
desiredDecimals,
resolvedTime,
txAnnotation.timestamp,
block
)

Expand All @@ -300,11 +312,6 @@ export default class EnrichmentService extends BaseService<Events> {
}
}

if (hasInsufficientFunds) {
txAnnotation.warnings ??= []
txAnnotation.warnings.push("insufficient-funds")
}

return txAnnotation
}

Expand Down Expand Up @@ -459,15 +466,13 @@ export default class EnrichmentService extends BaseService<Events> {
transaction: AnyEVMTransaction,
desiredDecimals: number
): Promise<EnrichedEVMTransaction> {
const enrichedTx = {
return {
...transaction,
annotation: await this.resolveTransactionAnnotation(
transaction.network,
transaction,
desiredDecimals
),
}

return enrichedTx
}
}

0 comments on commit 6fbcc36

Please sign in to comment.