Skip to content

Commit

Permalink
fix: tx history improvements (#229)
Browse files Browse the repository at this point in the history
  • Loading branch information
fmorency authored Feb 5, 2025
1 parent 1fe9bb3 commit b3137a0
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 68 deletions.
120 changes: 87 additions & 33 deletions components/bank/handlers/bank/msgMultiSendHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,68 +4,122 @@ import { registerHandler } from '@/components/bank/handlers/handlerRegistry';
import { BankIcon } from '@/components/icons/BankIcon';
import { format } from 'react-string-format';
import { TruncatedAddressWithCopy } from '@/components/react/addressCopy';
import { MetadataSDKType } from '@liftedinit/manifestjs/dist/codegen/cosmos/bank/v1beta1/bank';
import BigNumber from 'bignumber.js';
import {
Input,
MetadataSDKType,
Output,
} from '@liftedinit/manifestjs/dist/codegen/cosmos/bank/v1beta1/bank';
import { formatAmount, formatDenom, formatLargeNumber } from '@/utils';
import React from 'react';

const createMessage = (
const createSendMessage = (
template: string,
amount: string,
denom: string,
numReceivers: number,
inputs: Input[],
numReceiver: number,
color: string,
metadata?: MetadataSDKType[],
sender?: string
metadata?: MetadataSDKType[]
) => {
const formattedAmount = formatLargeNumber(formatAmount(amount, denom, metadata));
const formattedDenom = formatDenom(denom);
const coloredAmount = (
<span className={`text-${color}-500`}>
{formattedAmount} {formattedDenom}
</span>
);
const coloredDenom = <span className={`text-${color}-500`}>{formattedDenom}</span>;
let allAmountDenom: string[] = [];
// The CosmosSDK specified that only one input is allowed for MsgMultiSend
inputs?.[0]?.coins.forEach(coin => {
const amount = coin.amount;
const denom = coin.denom;
const formattedAmount = formatLargeNumber(formatAmount(amount, denom, metadata));
const formattedDenom = formatDenom(denom);
const amountDenom = formattedAmount + ' ' + formattedDenom;
allAmountDenom.push(amountDenom);
});

let displayAmountDenom: string;
if (allAmountDenom.length > 2) {
displayAmountDenom = `${allAmountDenom[0]}, ${allAmountDenom[allAmountDenom.length - 1]} and ${allAmountDenom.length - 2} more denomination(s)`;
} else {
displayAmountDenom = allAmountDenom.join(', ');
}

const coloredAmountDenom = <span className={`text-${color}-500`}>{displayAmountDenom}</span>;
const message = format(template, coloredAmountDenom, numReceiver);
return <span className="flex gap-1">{message}</span>;
};

const createReceiveMessage = (
template: string,
outputs: Output[],
address: string,
color: string,
sender: string,
metadata?: MetadataSDKType[]
) => {
const myOutputs = new Map<string, BigNumber>();
try {
outputs.forEach(output => {
if (output.address === address) {
// Compute the total amount of each denom received
output.coins.forEach(coin => {
myOutputs.set(
coin.denom,
BigNumber.sum(myOutputs.get(coin.denom) ?? 0, new BigNumber(coin.amount))
);
});
}
});
} catch (e) {
console.error('Error computing received amounts', e);
}

let allAmountDenom: string[] = [];
myOutputs.forEach((amount, denom) => {
const formattedAmount = formatLargeNumber(formatAmount(amount.toFixed(), denom, metadata));
const formattedDenom = formatDenom(denom);
const amountDenom = formattedAmount + ' ' + formattedDenom;
allAmountDenom.push(amountDenom);
});

let displayAmountDenom: string;
if (allAmountDenom.length > 2) {
displayAmountDenom = `${allAmountDenom[0]}, ${allAmountDenom[allAmountDenom.length - 1]} and ${allAmountDenom.length - 2} more denomination(s)`;
} else {
displayAmountDenom = allAmountDenom.join(', ');
}

const coloredAmountDenom = <span className={`text-${color}-500`}>{displayAmountDenom}</span>;
const message = format(
template,
coloredAmount,
numReceivers,
coloredDenom,
sender ? <TruncatedAddressWithCopy address={sender} slice={24} /> : 'an unknown address'
coloredAmountDenom,
<TruncatedAddressWithCopy address={sender} slice={24} />
);
return <span className="flex gap-1">{message}</span>;
};

export const MsgMultiSendHandler = createSenderReceiverHandler({
iconSender: BankIcon,
successSender: (tx, _, metadata) => {
return createMessage(
return createSendMessage(
'You sent {0} equally divided between {1} addresses',
tx.metadata?.inputs?.[0]?.coins?.[0]?.amount,
tx.metadata?.inputs?.[0]?.coins?.[0]?.denom,
tx.metadata?.inputs,
tx.metadata?.outputs?.length,
'red',
metadata
);
},
failSender: (tx, _, metadata) => {
return createMessage(
return createSendMessage(
'You failed to send {0} equally divided between {1} addresses',
tx.metadata?.inputs?.[0]?.coins?.[0]?.amount,
tx.metadata?.inputs?.[0]?.coins?.[0]?.denom,
tx.metadata?.inputs,
tx.metadata?.outputs?.length,
'red',
metadata
);
},
successReceiver: (tx, _, metadata) => {
return createMessage(
'You received {2} tokens from {3}',
tx.metadata?.inputs?.[0]?.coins?.[0]?.amount,
tx.metadata?.inputs?.[0]?.coins?.[0]?.denom,
tx.metadata?.outputs?.length,
successReceiver: (tx, address, metadata) => {
return createReceiveMessage(
'You received {0} from {1}',
tx.metadata?.outputs,
address,
'green',
metadata,
tx.sender
tx.sender,
metadata
);
},
});
Expand Down
26 changes: 22 additions & 4 deletions components/bank/handlers/group/msgSubmitProposalHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,36 @@ import { MsgSubmitProposal } from '@liftedinit/manifestjs/dist/codegen/cosmos/gr
import { TruncatedAddressWithCopy } from '@/components/react/addressCopy';
import { format } from 'react-string-format';

const createMessage = (template: string, ids: string, sender: string) => {
const message = format(template, ids, <TruncatedAddressWithCopy address={sender} slice={24} />);
const createMessage = (template: string, ids: string, policyAddress: string, sender?: string) => {
const message = format(
template,
ids,
policyAddress ? (
<TruncatedAddressWithCopy address={policyAddress} slice={24} />
) : (
'an unknown address'
),
sender ? <TruncatedAddressWithCopy address={sender} slice={24} /> : 'an unknown address'
);
return <span className="flex gap-1">{message}</span>;
};

export const MsgSubmitProposalHandler = createSenderReceiverHandler({
iconSender: GroupsIcon,
successSender: tx =>
createMessage('You submitted proposal #{0} to {1}', tx.proposal_ids?.[0], tx.sender), // TODO Link to proposal
createMessage(
'You submitted proposal #{0} to {1}',
tx.proposal_ids?.[0],
tx.metadata?.groupPolicyAddress
), // TODO Link to proposal
failSender: 'You failed to submit a proposal',
successReceiver: tx =>
createMessage('Proposal #{0} was submitted by {1}', tx.proposal_ids?.[0], tx.sender), // TODO Link to proposal
createMessage(
'Proposal #{0} was submitted to {1} by {2}',
tx.proposal_ids?.[0],
tx.metadata?.groupPolicyAddress,
tx.sender
), // TODO Link to proposal
});

registerHandler(MsgSubmitProposal.typeUrl, MsgSubmitProposalHandler);
105 changes: 75 additions & 30 deletions components/bank/handlers/manifest/msgPayoutHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,87 @@ import { createSenderReceiverHandler } from '../createSenderReceiverHandler';
import { registerHandler } from '@/components/bank/handlers/handlerRegistry';
import { MsgPayout } from '@liftedinit/manifestjs/dist/codegen/liftedinit/manifest/v1/tx';
import { TruncatedAddressWithCopy } from '@/components/react/addressCopy';
import { createTokenMessage } from '@/components';
import { format } from 'react-string-format';
import { MetadataSDKType } from '@liftedinit/manifestjs/dist/codegen/cosmos/bank/v1beta1/bank';
import BigNumber from 'bignumber.js';
import { formatAmount, formatDenom, formatLargeNumber } from '@/utils';
import { Coin } from '@liftedinit/manifestjs/dist/codegen';

// The PayoutPair code generated by Telescope is wrong
interface Pair {
address: string;
coin: Coin;
}
const createSendMessage = (
template: string,
pairs: Pair[],
color: string,
metadata?: MetadataSDKType[]
) => {
const sum = pairs.reduce((acc, pair) => acc.plus(pair.coin.amount), new BigNumber(0));
const denom = formatDenom(pairs?.[0]?.coin?.denom); // We can only mint umfx anyway
const coloredAmount = (
<span className={`text-${color}-500`}>
{formatLargeNumber(formatAmount(sum.toFixed(), denom, metadata))} {denom}
</span>
);
const recipient =
pairs.length > 1
? `distributed across ${pairs.length} addresses`
: pairs?.[0]?.address
? `to ${(<TruncatedAddressWithCopy address={pairs[0].address} slice={24} />)}`
: 'an unknown address';
const message = format(template, coloredAmount, recipient);
return <span className="flex gap-1">{message}</span>;
};

const createReceiveMessage = (
template: string,
pairs: Pair[],
color: string,
address: string,
metadata?: MetadataSDKType[],
sender?: string
) => {
const sum = pairs.reduce((acc, pair) => {
return pair.address === address ? acc.plus(pair.coin.amount) : acc;
}, new BigNumber(0));
const denom = formatDenom(pairs?.[0]?.coin?.denom); // We can only mint umfx anyway
const coloredAmount = (
<span className={`text-${color}-500`}>
{formatLargeNumber(formatAmount(sum.toFixed(), denom, metadata))} {denom}
</span>
);
const message = format(
template,
coloredAmount,
sender ? <TruncatedAddressWithCopy address={sender} slice={24} /> : 'an unknown address'
);
return <span className="flex gap-1">{message}</span>;
};

export const MsgPayoutHandler = createSenderReceiverHandler({
iconSender: MintIcon,
successSender: (tx, _, metadata) => {
return tx.metadata?.payoutPairs?.length > 1
? format('You minted tokens to {0} addresses', tx.metadata?.payoutPairs?.length)
: createTokenMessage(
'You minted {0} to {1}',
tx.metadata?.payoutPairs?.[0]?.coin?.amount,
tx.metadata?.payoutPairs?.[0]?.coin?.denom,
tx.metadata?.payoutPairs?.[0]?.address,
'green',
metadata
);
return createSendMessage('You minted {0} {1}', tx.metadata?.payoutPairs, 'green', metadata);
},
failSender: (tx, _, metadata) => {
return createSendMessage(
'You failed to mint {0} {1}',
tx.metadata?.payoutPairs,
'red',
metadata
);
},
failSender: 'You failed to mint tokens',
successReceiver: (tx, _, metadata) => {
return tx.metadata?.payoutPairs?.length > 1
? format(
'You were minted tokens by {0}',
tx.sender ? (
<TruncatedAddressWithCopy address={tx.sender} slice={24} />
) : (
'an unknown address'
)
)
: createTokenMessage(
'You were minted {0} by {1}',
tx.metadata?.payoutPairs?.[0]?.coin?.amount,
tx.metadata?.payoutPairs?.[0]?.coin?.denom,
tx.sender,
'green',
metadata
);
successReceiver: (tx, address, metadata) => {
return createReceiveMessage(
'You were minted {0} from {1}',
tx.metadata?.payoutPairs,
'green',
address,
metadata,
tx.sender
);
},
});

Expand Down
3 changes: 2 additions & 1 deletion components/bank/handlers/poa/msgSetPowerHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import { TruncatedAddressWithCopy } from '@/components/react/addressCopy';
import { format } from 'react-string-format';

const createMessage = (template: string, validatorAddress: string, power: number) => {
const realPower = power / 1000000;
const message = format(
template,
validatorAddress ? (
<TruncatedAddressWithCopy address={validatorAddress} slice={24} />
) : (
'unknown'
),
power
realPower
);
return <span className="flex gap-1">{message}</span>;
};
Expand Down

0 comments on commit b3137a0

Please sign in to comment.