From 86b7476cb705e5dbac5d93b372e9554ca1974111 Mon Sep 17 00:00:00 2001 From: avalkov Date: Tue, 9 Aug 2022 15:18:47 +0300 Subject: [PATCH] Show reward withdraw when delegating --- src/components/msg/index.ts | 5 + src/components/msg/mutate/index.tsx | 141 +++++++++++++++++++++++ src/screens/transaction_details/hooks.ts | 9 +- 3 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 src/components/msg/mutate/index.tsx diff --git a/src/components/msg/index.ts b/src/components/msg/index.ts index 0fbf4e5d50..d4c6083eee 100644 --- a/src/components/msg/index.ts +++ b/src/components/msg/index.ts @@ -7,6 +7,10 @@ import { convertMsgsToModels, } from './utils'; +import { + mutateMessages, +} from './mutate'; + // ========================= // msg components // ========================= @@ -80,6 +84,7 @@ export { getMessageModelByType, getMessageByType, convertMsgsToModels, + mutateMessages, }; export { diff --git a/src/components/msg/mutate/index.tsx b/src/components/msg/mutate/index.tsx new file mode 100644 index 0000000000..f2a6cb6235 --- /dev/null +++ b/src/components/msg/mutate/index.tsx @@ -0,0 +1,141 @@ +import * as R from 'ramda'; +import * as MODELS from '@models'; +import { getMessageModelByType } from '@msg'; + +export const mutateMessages = (transaction: any) => { + return insertMissingWithdrawRewardsMsgWhenDelegating(transaction); +}; + +const insertMissingWithdrawRewardsMsgWhenDelegating = (transaction: any) => { + const messages = R.pathOr([], ['messages'], transaction); + const logs = R.pathOr([], ['logs'], transaction); + + let isWithdrawMsgFound = false; + + for (let i = 0; i < messages.length; i += 1) { + const model = getMessageModelByType(messages[i]?.['@type']); + + if (model === MODELS.MsgWithdrawDelegatorReward) { + isWithdrawMsgFound = true; + break; + } + } + + if (isWithdrawMsgFound) { + return [messages, logs]; + } + + for (let i = 0; i < messages.length; i += 1) { + const model = getMessageModelByType(messages[i]?.['@type']); + + if (model === MODELS.MsgDelegate) { + const coinReceivedEvent = getEventAtIndex(transaction, i, 'coin_received'); + const coinSpentEvent = getEventAtIndex(transaction, i, 'coin_spent'); + + if (coinReceivedEvent && coinSpentEvent) { + const [withdrawMessage, events] = buildWithdrawRewardsMessage(messages[i], + coinReceivedEvent, coinSpentEvent); + + if (i > 0) { + i -= 1; + } + + messages.splice(i, 0, withdrawMessage); + logs.splice(i, 0, { events }); + + break; + } + } + } + + return [messages, logs]; +}; + +const getEventAtIndex = (transaction: any, index: number, eventType: string) => { + const logs = R.pathOr([], ['logs'], transaction); + if (logs.length <= index) { + return; + } + + const { events } = logs[index]; + + for (let i = 0; i < events.length; i += 1) { + if (events[i].type === eventType) { + return events[i]; + } + } +}; + +const buildWithdrawRewardsMessage = (delegateMsg: any, coinReceivedEvent: any, + coinSpentEvent: any) => { + const withdrawMsg = { + '@type': '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward', + delegator_address: delegateMsg.delegator_address, + validator_address: delegateMsg.validator_address, + }; + + const events = []; + + events.push(buildEvent('coin_received', [ + { + key: 'receiver', value: coinReceivedEvent.attributes[0].value, + }, + { + key: 'amount', value: coinReceivedEvent.attributes[1].value, + }, + ])); + + events.push(buildEvent('coin_spent', [ + { + key: 'spender', value: coinSpentEvent.attributes[0].value, + }, + { + key: 'amount', value: coinSpentEvent.attributes[1].value, + }, + ])); + + events.push(buildEvent('message', [ + { + key: 'action', value: '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward', + }, + { + key: 'sender', value: coinSpentEvent.attributes[0].value, + }, + { + key: 'module', value: 'distribution', + }, + { + key: 'sender', value: coinReceivedEvent.attributes[0].value, + }, + ])); + + events.push(buildEvent('transfer', [ + { + key: 'recipient', value: coinReceivedEvent.attributes[0].value, + }, + { + key: 'sender', value: coinSpentEvent.attributes[0].value, + }, + { + key: 'amount', value: coinReceivedEvent.attributes[1].value, + }, + ])); + + events.push(buildEvent('withdraw_rewards', [ + { + key: 'amount', value: coinReceivedEvent.attributes[1].value, + }, + { + key: 'validator', value: delegateMsg.validator_address, + }, + ])); + + return [withdrawMsg, events]; +}; + +const buildEvent = (eventType: any, attributes: any) => { + return { + type: eventType, + attributes, + }; +}; diff --git a/src/screens/transaction_details/hooks.ts b/src/screens/transaction_details/hooks.ts index 18750170eb..8366af7ee6 100644 --- a/src/screens/transaction_details/hooks.ts +++ b/src/screens/transaction_details/hooks.ts @@ -8,7 +8,10 @@ import { TransactionDetailsQuery, } from '@graphql/types'; import { formatToken } from '@utils/format_token'; -import { convertMsgsToModels } from '@msg'; +import { + convertMsgsToModels, mutateMessages, +} from '@msg'; + import { TransactionState, } from './types'; @@ -113,6 +116,10 @@ export const useTransactionDetails = () => { }; stateChange.logs = formatLogs(); + const [mutatedMessages, logs] = mutateMessages(data.transaction[0]); + data.transaction[0].messages = mutatedMessages; + data.transaction[0].logs = logs; + // ============================= // messages // =============================