Skip to content

Commit

Permalink
Add more interpreters (#86)
Browse files Browse the repository at this point in the history
* Add more interpreters

* Add changeset
  • Loading branch information
anastasiarods authored Aug 23, 2024
1 parent 6c8b91b commit 797da0f
Show file tree
Hide file tree
Showing 17 changed files with 675 additions and 93 deletions.
5 changes: 5 additions & 0 deletions .changeset/angry-adults-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@3loop/transaction-interpreter': minor
---

Add more default interpreters and std functions
46 changes: 46 additions & 0 deletions packages/transaction-interpreter/interpreters/1inch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { assetsReceived, assetsSent, displayAsset, getPayments, isSwap } from './std.js'
import type { InterpretedTransaction } from '@/types.js'
import type { DecodedTx } from '@3loop/transaction-decoder'

export function transformEvent(event: DecodedTx): InterpretedTransaction {
const methodName = event.methodCall.name

const newEvent: Omit<InterpretedTransaction, 'action' | 'type'> = {
chain: event.chainID,
txHash: event.txHash,
user: { address: event.fromAddress, name: null },
method: methodName,
assetsSent: assetsSent(event.transfers, event.fromAddress),
assetsReceived: assetsReceived(event.transfers, event.fromAddress),
}

const netSent = getPayments({
transfers: event.transfers,
fromAddresses: [event.fromAddress],
})

const netReceived = getPayments({
transfers: event.transfers,
toAddresses: [event.fromAddress],
})

if (isSwap(event)) {
return {
type: 'swap',
action: 'Swapped ' + displayAsset(netSent[0]) + ' for ' + displayAsset(netReceived[0]),
...newEvent,
}
}

return {
type: 'unknown',
action: `Called method '${methodName}'`,
...newEvent,
}
}

export const contracts = [
'1:0x111111125421cA6dc452d289314280a0f8842A65', // 1inchv6
'1:0x1111111254EEB25477B68fb85Ed929f73A960582', // 1inchv5
'1:0x1111111254fb6c44bAC0beD2854e76F90643097d', // 1inchv4
]
33 changes: 33 additions & 0 deletions packages/transaction-interpreter/interpreters/aa.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { InterpretedTransaction } from '@/types.js'
import { DecodedTx } from '@3loop/transaction-decoder'
import { assetsReceived, assetsSent, displayAddress, formatNumber } from './std.js'

export function transformEvent(event: DecodedTx): InterpretedTransaction {
const methodName = event.methodCall.name

const newEvent: Omit<InterpretedTransaction, 'action' | 'type'> = {
chain: event.chainID,
txHash: event.txHash,
user: { address: event.fromAddress, name: null },
method: methodName,
assetsSent: assetsSent(event.transfers, event.fromAddress),
assetsReceived: assetsReceived(event.transfers, event.fromAddress),
}

if (event.methodCall.name !== 'handleOps') return { type: 'unknown', action: 'Unknown action', ...newEvent }

const userOpEvents = event.interactions.filter((e) => e.event.eventName === 'UserOperationEvent')
const isBatch = userOpEvents.length > 1
const fee = newEvent.assetsReceived[0]?.amount
const sender = (userOpEvents[0].event.params as { sender: string }).sender

return {
type: 'account-abstraction',
action: `Account Abstraction transaction by ${
isBatch ? userOpEvents.length + ' adresses' : displayAddress(sender)
} with fee ${formatNumber(fee, 4)}`,
...newEvent,
}
}

export const contracts = ['1:0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789']
84 changes: 84 additions & 0 deletions packages/transaction-interpreter/interpreters/aave.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { assetsReceived, assetsSent, displayAsset, NULL_ADDRESS } from './std.js'
import type { InterpretedTransaction } from '@/types.js'
import type { DecodedTx } from '@3loop/transaction-decoder'

export function transformEvent(event: DecodedTx): InterpretedTransaction {
const methodName = event.methodCall.name

const newEvent: Omit<InterpretedTransaction, 'action' | 'type'> = {
chain: event.chainID,
txHash: event.txHash,
user: { address: event.fromAddress, name: null },
method: methodName,
assetsSent: assetsSent(event.transfers, event.fromAddress),
assetsReceived: assetsReceived(event.transfers, event.fromAddress),
}

const directSent = newEvent.assetsSent.filter((a) => a.from.address !== NULL_ADDRESS && a.to.address !== NULL_ADDRESS)
const directReceived = newEvent.assetsReceived.filter(
(a) => a.from.address !== NULL_ADDRESS && a.to.address !== NULL_ADDRESS,
)

switch (methodName) {
case 'repay':
case 'repayWithPermit':
case 'repayWithATokens':
return {
type: 'repay-loan',
action: 'User repaid ' + displayAsset(directSent[0]),
...newEvent,
}

case 'deposit':
case 'supplyWithPermit':
case 'supply':
return {
type: 'deposit-collateral',
action: 'User deposited ' + displayAsset(directSent[0]),
...newEvent,
}

case 'borrow':
return {
type: 'borrow',
action: 'User borrowed ' + displayAsset(directReceived[0]),
...newEvent,
}

case 'withdraw':
return {
type: 'withdraw-collateral',
action: 'User withdrew ' + displayAsset(directReceived[0]),
...newEvent,
}

case 'flashLoanSimple': {
return {
type: 'unknown',
action: 'Executed flash loan with ' + displayAsset(directSent[0]),
...newEvent,
}
}

case 'setUserUseReserveAsCollateral': {
const assetAddress = event.methodCall.arguments[0].value as string
const enabled = event.methodCall.arguments[1].value === 'true'
return {
type: 'set-user-use-reserve-as-collateral',
action: `User ${enabled ? 'enabled' : 'disabled'} ${assetAddress} as collateral`,
...newEvent,
}
}
}

return {
...newEvent,
type: 'unknown',
action: 'Unknown action',
}
}

export const contracts = [
'1:0x7d2768de32b0b80b7a3454c06bdac94a69ddc7a9', // Aave v2
'1:0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2', // Aave v3
]
54 changes: 0 additions & 54 deletions packages/transaction-interpreter/interpreters/aave2.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { assetsReceived, assetsSent } from './std.js'
import type { AssetTransfer, InterpretedTransaction } from '@/types.js'
import { assetsReceived, assetsSent, displayAsset, getPayments, isSwap } from './std.js'
import type { InterpretedTransaction } from '@/types.js'
import type { DecodedTx } from '@3loop/transaction-decoder'

function displayAsset(asset: AssetTransfer): string {
return asset.amount + ' ' + asset.asset.symbol
}

export function transformEvent(event: DecodedTx): InterpretedTransaction {
const methodName = event.methodCall.name

Expand All @@ -18,15 +14,22 @@ export function transformEvent(event: DecodedTx): InterpretedTransaction {
assetsReceived: assetsReceived(event.transfers, event.fromAddress),
}

switch (methodName) {
case 'uniswapV3Swap':
case 'swap':
case 'unoswap':
return {
type: 'swap',
action: 'Swapped ' + displayAsset(newEvent.assetsSent[0]) + ' for ' + displayAsset(newEvent.assetsReceived[0]),
...newEvent,
}
const netSent = getPayments({
transfers: event.transfers,
fromAddresses: [event.fromAddress],
})

const netReceived = getPayments({
transfers: event.transfers,
toAddresses: [event.fromAddress],
})

if (isSwap(event)) {
return {
type: 'swap',
action: 'Swapped ' + displayAsset(netSent[0]) + ' for ' + displayAsset(netReceived[0]),
...newEvent,
}
}

return {
Expand All @@ -36,4 +39,4 @@ export function transformEvent(event: DecodedTx): InterpretedTransaction {
}
}

export const contracts = ['1:0x1111111254EEB25477B68fb85Ed929f73A960582']
export const contracts = ['1:0x3328f7f4a1d1c57c35df56bbf0c9dcafca309c49']
66 changes: 66 additions & 0 deletions packages/transaction-interpreter/interpreters/blur.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { assetsReceived, assetsSent, displayPayments, processNftTransfers } from './std.js'
import type { InterpretedTransaction } from '@/types.js'
import type { DecodedTx } from '@3loop/transaction-decoder'

export function transformEvent(event: DecodedTx): InterpretedTransaction {
const methodName = event.methodCall.name ?? 'unknown'

const newEvent: Omit<InterpretedTransaction, 'action' | 'type'> = {
chain: event.chainID,
txHash: event.txHash,
user: { address: event.fromAddress, name: null },
method: methodName,
assetsSent: assetsSent(event.transfers, event.fromAddress),
assetsReceived: assetsReceived(event.transfers, event.fromAddress),
}

const { sendingAddresses, receivingAddresses, nftTransfers, erc20Payments, nativePayments } = processNftTransfers(
event.transfers,
)

if (nftTransfers.length === 0) {
return {
type: 'unknown',
action: 'Unknown action',
...newEvent,
}
}

const collection = nftTransfers[0].name ?? ''
const numberOfNfts = nftTransfers.length > 1 ? ` ${nftTransfers.length} ${collection} NFTS` : ` 1 ${collection} NFT`
const payment = displayPayments(erc20Payments, nativePayments)

const sell = ['takeBidSingle', 'takeBid']
const buy = ['takeAskSinglePool', 'takeAskSingle', 'takeAsk', 'takeAskPool', 'batchBuyWithETH', 'batchBuyWithERC20s']

if (sell.includes(methodName)) {
const from = receivingAddresses.length > 1 ? ` to ${receivingAddresses.length} users` : ''
return {
type: 'transfer-nft',
action: `Sold${numberOfNfts} for ${payment}${from}`,
...newEvent,
}
}

if (buy.includes(methodName)) {
const from = sendingAddresses.length > 1 ? ` from ${sendingAddresses.length} users` : ''
return {
type: 'transfer-nft',
action: `Bought${numberOfNfts} for ${payment}${from}`,
...newEvent,
}
}

return {
type: 'unknown',
action: 'Unknown action',
...newEvent,
}
}

export const contracts = [
//Blur v3
'1:0xb2ecfE4E4D61f8790bbb9DE2D1259B9e2410CEA5',
//Blur v2
'1:0x39da41747a83aeE658334415666f3EF92DD0D541',
]
12 changes: 6 additions & 6 deletions packages/transaction-interpreter/interpreters/erc20.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assetsReceived, assetsSent } from './std.js'
import { assetsReceived, assetsSent, formatNumber } from './std.js'
import type { InterpretedTransaction } from '@/types.js'
import type { DecodedTx } from '@3loop/transaction-decoder'

Expand Down Expand Up @@ -32,7 +32,7 @@ export function transformEvent(event: DecodedTx): InterpretedTransaction {
action = `Revoked approval for ${name} to be spent`
} else {
const amount = formatAmount(approvalValue, approval?.decimals || 18)
action = `Approved ${amount}${name} to be spent`
action = `Approved ${formatNumber(amount)}${name} to be spent`
}
}

Expand All @@ -43,25 +43,25 @@ export function transformEvent(event: DecodedTx): InterpretedTransaction {
}
}
case 'transfer': {
const amount = newEvent.assetsSent?.[0]?.amount || event.methodCall?.arguments?.[1]?.value
const amount = newEvent.assetsSent?.[0]?.amount || event.methodCall?.arguments?.[1]?.value || '0'
const symbol = newEvent.assetsSent?.[0]?.asset?.symbol || event.contractName || 'unknown'

return {
type: 'transfer-token',
action: `Sent ${amount} ${symbol}`,
action: `Sent ${formatNumber(amount.toString())} ${symbol}`,
...newEvent,
}
}
case 'transferFrom': {
const from = event.methodCall?.arguments?.[0]?.value as string
const amount = event.transfers[0]?.amount || event.methodCall?.arguments?.[2]?.value
const amount = event.transfers[0]?.amount || event.methodCall?.arguments?.[2]?.value || '0'
const symbol = event.transfers[0]?.symbol || event.contractName || 'unknown'

if (!from) break

return {
type: 'transfer-token',
action: `Sent ${amount} ${symbol}`,
action: `Sent ${formatNumber(amount.toString())} ${symbol}`,
...newEvent,
assetsSent: assetsSent(event.transfers, from),
assetsReceived: assetsReceived(event.transfers, from),
Expand Down
Loading

0 comments on commit 797da0f

Please sign in to comment.