-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add erc20 and nfts interpreters (#82)
* Add erc20 and nfts interpreters * Add changeset
- Loading branch information
1 parent
5696972
commit d1579f1
Showing
12 changed files
with
304 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@3loop/transaction-interpreter': minor | ||
--- | ||
|
||
Add default interpreters for erc20, erc721 and erc1155 contracts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { assetsReceived, assetsSent } 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), | ||
} | ||
|
||
switch (methodName) { | ||
case 'setApprovalForAll': { | ||
const nftName = event?.contractName ? event?.contractName + ' ' : '' | ||
const approvalValue = event.methodCall?.arguments?.[1]?.value | ||
|
||
if (approvalValue === 'true') { | ||
return { | ||
type: 'approve-nft', | ||
action: `Approved all NFTs ${nftName}to be spent`, | ||
...newEvent, | ||
} | ||
} else { | ||
return { | ||
type: 'approve-nft', | ||
action: `Revoked approval for all NFTs ${nftName}to be spent`, | ||
...newEvent, | ||
} | ||
} | ||
} | ||
case 'safeTransferFrom': { | ||
const from = (event.methodCall?.arguments?.[0]?.value as string) || '' | ||
const name = event.contractName | ||
const tokenId = event.methodCall?.arguments?.[2]?.value | ||
|
||
if (!name || !tokenId) break | ||
|
||
return { | ||
type: 'transfer-nft', | ||
action: `Sent ${name} #${tokenId}`, | ||
...newEvent, | ||
assetsSent: assetsSent(event.transfers, from), | ||
assetsReceived: assetsReceived(event.transfers, from), | ||
} | ||
} | ||
case 'safeBatchTransferFrom': { | ||
const from = (event.methodCall?.arguments?.[0]?.value as string) || '' | ||
const name = event.contractName | ||
const tokenIds = event.methodCall?.arguments?.[2]?.value as string[] | ||
|
||
if (!name || !tokenIds) break | ||
|
||
return { | ||
type: 'transfer-nft', | ||
action: 'Sent ' + name + ' ' + tokenIds.map((id) => `#${id}`).join(', '), | ||
...newEvent, | ||
assetsSent: assetsSent(event.transfers, from), | ||
assetsReceived: assetsReceived(event.transfers, from), | ||
} | ||
} | ||
} | ||
|
||
return { | ||
type: 'unknown', | ||
action: `Called method '${methodName}'`, | ||
...newEvent, | ||
} | ||
} | ||
|
||
export const contractType = 'erc1155' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { assetsReceived, assetsSent } 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), | ||
} | ||
|
||
switch (methodName) { | ||
case 'approve': { | ||
const approval = event.interactions.find((i) => i.event.eventName === 'Approval') | ||
const approvalValue = (event.methodCall?.arguments?.[1]?.value || '') as string | ||
const name = approval?.contractSymbol || event.contractName || 'unknown' | ||
let action = '' | ||
|
||
const isUnlimitedApproval = (value: string) => value.startsWith('11579208923731619542357098') | ||
const isRevokedApproval = (value: string) => value === '0' | ||
const formatAmount = (value: string, decimals: number) => | ||
(BigInt(value) / BigInt(10 ** decimals)).toString() + ' ' | ||
|
||
if (approvalValue) { | ||
if (isUnlimitedApproval(approvalValue)) { | ||
action = `Approved unlimited amount of ${name} to be spent` | ||
} else if (isRevokedApproval(approvalValue)) { | ||
action = `Revoked approval for ${name} to be spent` | ||
} else { | ||
const amount = formatAmount(approvalValue, approval?.decimals || 18) | ||
action = `Approved ${amount}${name} to be spent` | ||
} | ||
} | ||
|
||
return { | ||
type: 'approve-token', | ||
action, | ||
...newEvent, | ||
} | ||
} | ||
case 'transfer': { | ||
const amount = newEvent.assetsSent?.[0]?.amount || event.methodCall?.arguments?.[1]?.value | ||
const symbol = newEvent.assetsSent?.[0]?.asset?.symbol || event.contractName || 'unknown' | ||
|
||
return { | ||
type: 'transfer-token', | ||
action: `Sent ${amount} ${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 symbol = event.transfers[0]?.symbol || event.contractName || 'unknown' | ||
|
||
if (!from) break | ||
|
||
return { | ||
type: 'transfer-token', | ||
action: `Sent ${amount} ${symbol}`, | ||
...newEvent, | ||
assetsSent: assetsSent(event.transfers, from), | ||
assetsReceived: assetsReceived(event.transfers, from), | ||
} | ||
} | ||
} | ||
|
||
return { | ||
type: 'unknown', | ||
action: `Called method '${methodName}'`, | ||
...newEvent, | ||
} | ||
} | ||
|
||
export const contractType = 'erc20' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import { assetsReceived, assetsSent } 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), | ||
} | ||
|
||
switch (methodName) { | ||
case 'approve': { | ||
const nftName = event.contractName || '' | ||
const tokenId = event.methodCall?.arguments?.[1]?.value || '' | ||
|
||
return { | ||
type: 'approve-nft', | ||
action: `Approved NFT ${nftName ? `${nftName} ` : ''}${tokenId ? `#${tokenId} ` : ''}to be spent`, | ||
...newEvent, | ||
} | ||
} | ||
|
||
case 'setApprovalForAll': { | ||
const nftName = event?.contractName ? event?.contractName + ' ' : '' | ||
const approvalValue = event.methodCall?.arguments?.[1]?.value | ||
|
||
if (approvalValue === 'true') { | ||
return { | ||
type: 'approve-nft', | ||
action: `Approved all NFTs ${nftName}to be spent`, | ||
...newEvent, | ||
} | ||
} else { | ||
return { | ||
type: 'approve-nft', | ||
action: `Revoked approval for all NFTs ${nftName}to be spent`, | ||
...newEvent, | ||
} | ||
} | ||
} | ||
case 'safeTransferFrom': { | ||
const from = (event.methodCall?.arguments?.[0]?.value as string) || '' | ||
const name = event.contractName | ||
const tokenId = event.methodCall?.arguments?.[2]?.value | ||
|
||
if (!name || !tokenId) break | ||
|
||
return { | ||
type: 'transfer-nft', | ||
action: `Sent ${name} #${tokenId}`, | ||
...newEvent, | ||
assetsSent: assetsSent(event.transfers, from), | ||
assetsReceived: assetsReceived(event.transfers, from), | ||
} | ||
} | ||
case 'transferFrom': { | ||
const from = (event.methodCall?.arguments?.[0]?.value as string) || '' | ||
const name = event.contractName | ||
const tokenId = event.methodCall?.arguments?.[2]?.value | ||
|
||
if (!name || !tokenId) break | ||
|
||
return { | ||
type: 'transfer-nft', | ||
action: `Sent ${name} #${tokenId}`, | ||
...newEvent, | ||
assetsSent: assetsSent(event.transfers, from), | ||
assetsReceived: assetsReceived(event.transfers, from), | ||
} | ||
} | ||
} | ||
|
||
return { | ||
type: 'unknown', | ||
action: `Called method '${methodName}'`, | ||
...newEvent, | ||
} | ||
} | ||
|
||
export const contractType = 'erc721' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,36 @@ | ||
import { DecodedTx } from '@3loop/transaction-decoder' | ||
|
||
const interpretations: Record<string, string> = { | ||
/**PLACE_INTEPRETATIONS**/ | ||
} | ||
const contractToName: Record<string, string> = { | ||
/**PLACE_CONTRACT_MAPPING**/ | ||
} | ||
|
||
const contractTypeToName: Record<string, string> = { | ||
/**PLACE_CONTRACT_TYPE_MAPPING**/ | ||
} | ||
|
||
const standardLibrary = '/**PLACE_STD_CONTENT**/' | ||
const fallbackInterpreter = standardLibrary + '\n' + '/**PLACE_FALLBACK_CONTENT**/' | ||
|
||
// TODO: Add a default interpreter as a fallback | ||
function getInterpreterForContract({ address, chain }: { address: string; chain: number }): string | undefined { | ||
const key = `${chain}:${address}`.toLowerCase() | ||
function getInterpreter(tx: DecodedTx): string | undefined { | ||
const { chainID, toAddress, contractType } = tx | ||
const key = `${chainID}:${toAddress}`.toLowerCase() | ||
const id = contractToName[key] | ||
if (!id) { | ||
return undefined | ||
if (id) { | ||
return `${standardLibrary} \n ${interpretations[id]}` | ||
} | ||
return `${standardLibrary} \n ${interpretations[id]}` | ||
|
||
const contractTypes = ['ERC20', 'ERC721', 'ERC1155'] | ||
|
||
if (contractTypes.includes(contractType)) { | ||
const typeId = contractTypeToName[contractType.toLowerCase()] | ||
return `${standardLibrary} \n ${interpretations[typeId]}` | ||
} | ||
|
||
return undefined | ||
} | ||
|
||
export { getInterpreterForContract, fallbackInterpreter } | ||
export { getInterpreter, fallbackInterpreter } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.