From 58b50b90f0852a3390f0339b679f0a50edd33e9d Mon Sep 17 00:00:00 2001 From: ponyjackal Date: Mon, 1 Jul 2024 10:47:04 -0700 Subject: [PATCH] feat: add plotAction contextualizer --- .../protocol/cropXYZ/abis/PlotAction.ts | 188 ++++++++++++++++++ .../protocol/cropXYZ/constants.ts | 4 + .../protocol/cropXYZ/plotAction.ts | 155 +++++++++++++++ .../contextAction/protocolContextAction.ts | 8 +- 4 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 src/contextualizers/protocol/cropXYZ/abis/PlotAction.ts create mode 100644 src/contextualizers/protocol/cropXYZ/plotAction.ts diff --git a/src/contextualizers/protocol/cropXYZ/abis/PlotAction.ts b/src/contextualizers/protocol/cropXYZ/abis/PlotAction.ts new file mode 100644 index 00000000..51a9fe8f --- /dev/null +++ b/src/contextualizers/protocol/cropXYZ/abis/PlotAction.ts @@ -0,0 +1,188 @@ +const abi = [ + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'player', + type: 'address', + }, + { + indexed: true, + internalType: 'uint256', + name: 'plotId', + type: 'uint256', + }, + { + indexed: true, + internalType: 'uint256', + name: 'stakedElement', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'newStakedElement', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint16', + name: 'harvestableAmount', + type: 'uint16', + }, + { + indexed: false, + internalType: 'uint16', + name: 'actuallyProducedAmount', + type: 'uint16', + }, + { + indexed: false, + internalType: 'uint24', + name: 'plotHarvests', + type: 'uint24', + }, + ], + name: 'HarvestedPlot', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'player', + type: 'address', + }, + { + indexed: true, + internalType: 'uint256', + name: 'plotId', + type: 'uint256', + }, + { + indexed: true, + internalType: 'uint256', + name: 'diedStakedElement', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'newStakedElement', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint16', + name: 'diedAmount', + type: 'uint16', + }, + { + indexed: false, + internalType: 'uint24', + name: 'plotDeaths', + type: 'uint24', + }, + ], + name: 'ClearedDiedHarvest', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'player', + type: 'address', + }, + { + indexed: true, + internalType: 'uint256', + name: 'plotId', + type: 'uint256', + }, + { + indexed: true, + internalType: 'uint256', + name: 'clearedStakedElement', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'newStakedElement', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint16', + name: 'clearedAmount', + type: 'uint16', + }, + { + indexed: false, + internalType: 'uint24', + name: 'plotClears', + type: 'uint24', + }, + ], + name: 'ClearedHarvest', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'player', + type: 'address', + }, + { + indexed: true, + internalType: 'uint256', + name: 'plotId', + type: 'uint256', + }, + { + indexed: true, + internalType: 'uint256', + name: 'stakedElement', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint16', + name: 'stakedAmount', + type: 'uint16', + }, + { + indexed: false, + internalType: 'uint64', + name: 'timeStartStaked', + type: 'uint64', + }, + { + indexed: false, + internalType: 'uint64', + name: 'timeReadyDelta', + type: 'uint64', + }, + { + indexed: false, + internalType: 'uint64', + name: 'timeExpiredDelta', + type: 'uint64', + }, + ], + name: 'StakedCrop', + type: 'event', + }, +] as const; + +export default abi; diff --git a/src/contextualizers/protocol/cropXYZ/constants.ts b/src/contextualizers/protocol/cropXYZ/constants.ts index f803d55d..f4856dbd 100644 --- a/src/contextualizers/protocol/cropXYZ/constants.ts +++ b/src/contextualizers/protocol/cropXYZ/constants.ts @@ -1,5 +1,6 @@ import packActivationDestinationAbi from './abis/PackActivationDestination'; import packActivationSourceAbi from './abis/PackActivationSource'; +import plotActionAbi from './abis/PlotAction'; export const PACK_ACTIVATION_DESTINATION_CONTRACT = '0x21170f8bd35d0afa8ad55719ce29d6489a8585db'; @@ -9,6 +10,9 @@ export const PLOT_ERC721_CONTRACT = '0xe2f275b2a5c376fd10006b67a9be0cc3bd5488e8'; export const Z_GOLD_CONTRACT_ADDRESS = '0x387d73bd8682dceb3327b940213d5de50ee2bba2'; +export const PLOT_ACTION_CONTRACT_ADDRESS = + '0xB45805566A842efB6329c11e092158f3E0eDdaa2'; export const PACK_ACTIVATION_SOURCE_ABI = packActivationSourceAbi; export const PACK_ACTIVATION_DESTINATION_ABI = packActivationDestinationAbi; +export const PLOT_ACTION_ABI = plotActionAbi; diff --git a/src/contextualizers/protocol/cropXYZ/plotAction.ts b/src/contextualizers/protocol/cropXYZ/plotAction.ts new file mode 100644 index 00000000..1345f1cc --- /dev/null +++ b/src/contextualizers/protocol/cropXYZ/plotAction.ts @@ -0,0 +1,155 @@ +import { + Transaction, + EventLogTopics, + GoldContextActionEnum, +} from '../../../types'; +import { PLOT_ACTION_CONTRACT_ADDRESS, PLOT_ACTION_ABI } from './constants'; +import { decodeLog } from '../../../helpers/utils'; + +export function contextualize(transaction: Transaction): Transaction { + const isPackActivationSource = detect(transaction); + if (!isPackActivationSource) return transaction; + + const result = generate(transaction); + return result; +} + +export function detect(transaction: Transaction): boolean { + /** + * There is a degree of overlap between the 'detect' and 'generateContext' functions, + * and while this might seem redundant, maintaining the 'detect' function aligns with + * established patterns in our other modules. This consistency is beneficial, + * and it also serves to decouple the logic, thereby simplifying the testing process + */ + // check logs + if (!transaction.logs) return false; + + for (const log of transaction.logs) { + if (log.address !== PLOT_ACTION_CONTRACT_ADDRESS) continue; + + const decoded = decodeLog(PLOT_ACTION_ABI, log.data, [ + log.topic0, + log.topic1, + log.topic2, + log.topic3, + ] as EventLogTopics); + + if ( + decoded && + (decoded.eventName === 'HarvestedPlot' || + decoded.eventName === 'ClearedDiedHarvest' || + decoded.eventName === 'ClearedHarvest' || + decoded.eventName === 'StakedCrop') + ) { + return true; + } + } + + return false; +} + +export function generate(transaction: Transaction): Transaction { + if (!transaction.logs || !transaction.chainId) return transaction; + + // decode ActivatedStarterPackOnSource event + let decoded; + for (const log of transaction.logs) { + if (log.address !== PLOT_ACTION_CONTRACT_ADDRESS) continue; + + decoded = decodeLog(PLOT_ACTION_ABI, log.data, [ + log.topic0, + log.topic1, + log.topic2, + log.topic3, + ] as EventLogTopics); + + if (decoded && decoded.eventName === 'HarvestedPlot') { + const player = decoded.args['player']; + const plotId = decoded.args['plotId']; + transaction.context = { + summaries: { + category: 'PROTOCOL_1', + en: { + title: `Gold`, + default: '[[player]][[harvested]]plots[[plotId]]', + }, + }, + variables: { + player: { + type: 'address', + value: player, + }, + plotId: { + type: 'number', + value: plotId, + }, + harvested: { + type: 'contextAction', + value: GoldContextActionEnum.HARVESTED_PLOT, + }, + }, + }; + return transaction; + } + + if (decoded && decoded.eventName === 'ClearedHarvest') { + const player = decoded.args['player']; + const plotId = decoded.args['plotId']; + transaction.context = { + summaries: { + category: 'PROTOCOL_1', + en: { + title: `Gold`, + default: '[[player]][[clearedHarvest]]plots[[plotId]]', + }, + }, + variables: { + player: { + type: 'address', + value: player, + }, + plotId: { + type: 'number', + value: plotId, + }, + clearedHarvest: { + type: 'contextAction', + value: GoldContextActionEnum.CLEARED_HARVEST, + }, + }, + }; + return transaction; + } + + if (decoded && decoded.eventName === 'StakedCrop') { + const player = decoded.args['player']; + const plotId = decoded.args['plotId']; + transaction.context = { + summaries: { + category: 'PROTOCOL_1', + en: { + title: `Gold`, + default: '[[player]][[stakedCrop]]plots[[plotId]]', + }, + }, + variables: { + player: { + type: 'address', + value: player, + }, + plotId: { + type: 'number', + value: plotId, + }, + stakedCrop: { + type: 'contextAction', + value: GoldContextActionEnum.STAKED_CROP, + }, + }, + }; + return transaction; + } + } + + return transaction; +} diff --git a/src/types/contextAction/protocolContextAction.ts b/src/types/contextAction/protocolContextAction.ts index 7a9e17ac..533ae731 100644 --- a/src/types/contextAction/protocolContextAction.ts +++ b/src/types/contextAction/protocolContextAction.ts @@ -230,11 +230,17 @@ export type BoomboxContextAction = export enum GoldContextActionEnum { ACTIVATED_A_STARTER_PACK = 'ACTIVATED_A_STARTER_PACK', RECEIVED = 'RECEIVED', + HARVESTED_PLOT = 'HARVESTED_PLOT', + CLEARED_HARVEST = 'CLEARED_HARVEST', + STAKED_CROP = 'STAKED_CROP', } export type GoldContextAction = | GoldContextActionEnum.ACTIVATED_A_STARTER_PACK - | GoldContextActionEnum.RECEIVED; + | GoldContextActionEnum.RECEIVED + | GoldContextActionEnum.HARVESTED_PLOT + | GoldContextActionEnum.CLEARED_HARVEST + | GoldContextActionEnum.STAKED_CROP; export enum Protocols { WETH = 'WETH',