|
1 | 1 | import { Box, Flex, Stack, Text, atoms } from '@zoralabs/zord' |
2 | | -import axios from 'axios' |
| 2 | +import { toLower } from 'lodash' |
| 3 | +import { get } from 'lodash' |
3 | 4 | import React, { Fragment } from 'react' |
4 | | -import useSWR from 'swr' |
5 | | -import { formatEther } from 'viem' |
| 5 | +import { decodeAbiParameters, formatEther, parseAbiParameters } from 'viem' |
6 | 6 |
|
7 | 7 | import { ETHERSCAN_BASE_URL } from 'src/constants/etherscan' |
8 | | -import SWR_KEYS from 'src/constants/swrKeys' |
| 8 | +import { getEscrowBundler } from 'src/modules/create-proposal/components/TransactionForm/Escrow/EscrowUtils' |
9 | 9 | import { useChainStore } from 'src/stores/useChainStore' |
10 | | -import { CHAIN_ID } from 'src/typings' |
11 | 10 | import { walletSnippet } from 'src/utils/helpers' |
| 11 | +import { DecodedTransaction } from './useDecodedTransactions' |
12 | 12 |
|
13 | 13 | interface DecodedTransactionProps { |
14 | | - targets: string[] |
15 | | - calldatas: string[] |
16 | | - values: string[] |
| 14 | + decodedTransactions: DecodedTransaction[] | undefined |
17 | 15 | } |
| 16 | + |
18 | 17 | export const DecodedTransactions: React.FC<DecodedTransactionProps> = ({ |
19 | | - targets, |
20 | | - calldatas, |
21 | | - values, |
| 18 | + decodedTransactions, |
22 | 19 | }) => { |
23 | 20 | const chain = useChainStore((x) => x.chain) |
24 | 21 |
|
25 | | - /* |
26 | | - |
27 | | - format in shape defined in ethers actor |
28 | | - |
29 | | - */ |
30 | | - const formatSendEth = (value: string) => { |
31 | | - const amount = formatEther(BigInt(value)) |
32 | | - return { |
33 | | - functionName: 'Transfer', |
34 | | - name: 'Transfer', |
35 | | - args: { |
36 | | - ['Transfer']: { name: `value`, value: `${amount} ETH` }, |
37 | | - }, |
38 | | - } |
39 | | - } |
40 | | - |
41 | | - const decodeTransaction = async ( |
42 | | - chainId: CHAIN_ID, |
43 | | - target: string, |
44 | | - calldata: string, |
45 | | - value: string |
46 | | - ) => { |
47 | | - /* if calldata is '0x' */ |
48 | | - const isTransfer = calldata === '0x' |
| 22 | + const renderArgument = (arg: any) => { |
| 23 | + if (arg?.name === 'escrowData') { |
| 24 | + // Decode escrow data |
| 25 | + const decodedAbiData = decodeAbiParameters( |
| 26 | + parseAbiParameters([ |
| 27 | + 'address client', |
| 28 | + 'address resolver', |
| 29 | + 'uint8 resolverType', |
| 30 | + 'address token', |
| 31 | + 'uint256 terminationTime', |
| 32 | + 'bytes32 details', |
| 33 | + 'address provider', |
| 34 | + 'address providerReceiver', |
| 35 | + 'bool requireVerification', |
| 36 | + 'bytes32 escrowType', |
| 37 | + ]), |
| 38 | + arg.value |
| 39 | + ) |
49 | 40 |
|
50 | | - if (isTransfer) { |
51 | | - return formatSendEth(value) |
| 41 | + return ( |
| 42 | + <Stack pl={'x2'} gap={'x1'}> |
| 43 | + <Flex>client/signer: {get(decodedAbiData, '[0]', '')}</Flex> |
| 44 | + <Flex>resolver: {get(decodedAbiData, '[1]', '')}</Flex> |
| 45 | + <Flex> |
| 46 | + Safety Valve Date:{' '} |
| 47 | + {new Date(Number(get(decodedAbiData, '[4]', 0)) * 1000).toLocaleString()} |
| 48 | + </Flex> |
| 49 | + <Flex>Service Provider: {get(decodedAbiData, '[6]', '')}</Flex> |
| 50 | + </Stack> |
| 51 | + ) |
52 | 52 | } |
53 | 53 |
|
54 | | - try { |
55 | | - const decoded = await axios.post('/api/decode', { |
56 | | - calldata: calldata, |
57 | | - contract: target, |
58 | | - chain: chainId, |
59 | | - }) |
60 | | - |
61 | | - if (decoded?.data?.statusCode) throw new Error('Decode failed') |
62 | | - |
63 | | - return decoded.data |
64 | | - } catch (err) { |
65 | | - console.log('err', err) |
66 | | - |
67 | | - // if this tx has value display it as a send eth tx |
68 | | - if (value.length && parseInt(value)) return formatSendEth(value) |
69 | | - |
70 | | - // if no value return original calldata |
71 | | - return calldata |
72 | | - } |
| 54 | + return ( |
| 55 | + <Flex key={arg?.name}> |
| 56 | + {arg?.name}:{' '} |
| 57 | + {arg?.name === '_milestoneAmounts' |
| 58 | + ? arg.value |
| 59 | + .split(',') |
| 60 | + .map((amt: string) => `${formatEther(BigInt(amt))} ETH`) |
| 61 | + .join(', ') |
| 62 | + : arg?.name === '_fundAmount' |
| 63 | + ? formatEther(BigInt(arg?.value)) + ' ETH' |
| 64 | + : arg?.value} |
| 65 | + </Flex> |
| 66 | + ) |
73 | 67 | } |
74 | 68 |
|
75 | | - const { data: decodedTransactions } = useSWR( |
76 | | - targets && calldatas && values |
77 | | - ? [SWR_KEYS.PROPOSALS_TRANSACTIONS, targets, calldatas, values] |
78 | | - : null, |
79 | | - async (_, targets, calldatas, values) => { |
80 | | - return await Promise.all( |
81 | | - targets.map(async (target, i) => { |
82 | | - const transaction = await decodeTransaction( |
83 | | - chain.id, |
84 | | - target, |
85 | | - calldatas[i], |
86 | | - values[i] |
87 | | - ) |
88 | | - |
89 | | - return { |
90 | | - target, |
91 | | - transaction, |
92 | | - isNotDecoded: transaction === calldatas[i], |
93 | | - } |
94 | | - }) |
95 | | - ) |
96 | | - }, |
97 | | - { revalidateOnFocus: false } |
98 | | - ) |
99 | | - |
100 | 69 | return ( |
101 | 70 | <Stack style={{ maxWidth: 600, wordBreak: 'break-word' }}> |
102 | 71 | <ol> |
103 | | - {decodedTransactions?.map((decoded, i) => ( |
104 | | - <Fragment key={`${decoded.target}-${i}`}> |
105 | | - {decoded.isNotDecoded ? ( |
106 | | - <li className={atoms({ paddingBottom: 'x4' })}>{decoded.transaction}</li> |
107 | | - ) : ( |
108 | | - <li className={atoms({ paddingBottom: 'x4' })}> |
109 | | - <Stack> |
110 | | - <Stack gap={'x1'}> |
111 | | - <Box |
112 | | - color={'secondary'} |
113 | | - fontWeight={'heading'} |
114 | | - className={atoms({ textDecoration: 'underline' })} |
115 | | - > |
116 | | - <a |
117 | | - href={`${ETHERSCAN_BASE_URL[chain.id]}/address/${ |
118 | | - decoded?.target |
119 | | - }`} |
120 | | - target="_blank" |
121 | | - rel="noreferrer" |
| 72 | + {decodedTransactions?.map((decoded, i) => { |
| 73 | + const isEscrow = toLower(decoded.target).includes(toLower(getEscrowBundler(chain.id))) |
| 74 | + |
| 75 | + return ( |
| 76 | + <Fragment key={`${decoded.target}-${i}`}> |
| 77 | + {decoded.isNotDecoded ? ( |
| 78 | + <li className={atoms({ paddingBottom: 'x4' })}>{decoded.transaction.toString()}</li> |
| 79 | + ) : ( |
| 80 | + <li className={atoms({ paddingBottom: 'x4' })}> |
| 81 | + <Stack> |
| 82 | + <Stack gap={'x1'}> |
| 83 | + <Box |
| 84 | + color={'secondary'} |
| 85 | + fontWeight={'heading'} |
| 86 | + className={atoms({ textDecoration: 'underline' })} |
122 | 87 | > |
123 | | - <Text display={{ '@initial': 'flex', '@768': 'none' }}> |
124 | | - {walletSnippet(decoded?.target)} |
125 | | - </Text> |
126 | | - <Text display={{ '@initial': 'none', '@768': 'flex' }}> |
127 | | - {decoded?.target} |
128 | | - </Text> |
129 | | - </a> |
130 | | - </Box> |
131 | | - <Flex pl={'x2'}> |
132 | | - {`.${decoded?.transaction?.functionName}(`} |
133 | | - {!decoded?.transaction?.args && |
134 | | - !decoded.transaction.decoded.length && |
| 88 | + <a |
| 89 | + href={`${ETHERSCAN_BASE_URL[chain.id]}/address/${decoded?.target |
| 90 | + }`} |
| 91 | + target="_blank" |
| 92 | + rel="noreferrer" |
| 93 | + > |
| 94 | + <Text display={{ '@initial': 'flex', '@768': 'none' }}> |
| 95 | + {walletSnippet(decoded?.target)} |
| 96 | + </Text> |
| 97 | + <Text display={{ '@initial': 'none', '@768': 'flex' }}> |
| 98 | + {decoded?.target} |
| 99 | + </Text> |
| 100 | + </a> |
| 101 | + </Box> |
| 102 | + <Flex pl={'x2'}> |
| 103 | + {`.${!isEscrow ? decoded?.transaction?.functionName : 'deployEscrow' |
| 104 | + }(`} |
| 105 | + {!decoded?.transaction?.args && |
| 106 | + !decoded.transaction?.decoded?.length && |
| 107 | + `)`} |
| 108 | + </Flex> |
| 109 | + |
| 110 | + <Stack pl={'x4'} gap={'x1'}> |
| 111 | + {(decoded?.transaction?.args && |
| 112 | + Object?.values(decoded?.transaction?.args).map((arg: any) => |
| 113 | + renderArgument(arg) |
| 114 | + )) || |
| 115 | + (decoded?.transaction?.decoded && |
| 116 | + decoded?.transaction?.decoded?.map((arg: any) => ( |
| 117 | + <Flex key={arg}>{arg}</Flex> |
| 118 | + )))} |
| 119 | + </Stack> |
| 120 | + |
| 121 | + {(!!decoded?.transaction?.args || |
| 122 | + !!decoded?.transaction?.decoded?.length) && |
135 | 123 | `)`} |
136 | | - </Flex> |
137 | | - <Stack pl={'x4'} gap={'x1'}> |
138 | | - {(decoded?.transaction?.args && |
139 | | - Object?.values(decoded?.transaction?.args).map((arg: any) => ( |
140 | | - // if verified contract and arguments object {name, value} |
141 | | - <Flex key={arg?.name}> |
142 | | - {arg?.name}: {arg?.value} |
143 | | - </Flex> |
144 | | - ))) || |
145 | | - // if unverified contract and arguments array [value] |
146 | | - (decoded?.transaction?.decoded && |
147 | | - decoded?.transaction?.decoded?.map((arg: any) => ( |
148 | | - <Flex key={arg}>{arg}</Flex> |
149 | | - )))} |
150 | 124 | </Stack> |
151 | | - {(!!decoded?.transaction?.args || |
152 | | - !!decoded?.transaction.decoded.length) && |
153 | | - `)`} |
154 | 125 | </Stack> |
155 | | - </Stack> |
156 | | - </li> |
157 | | - )} |
158 | | - </Fragment> |
159 | | - ))} |
| 126 | + </li> |
| 127 | + )} |
| 128 | + </Fragment> |
| 129 | + ); |
| 130 | + })} |
160 | 131 | </ol> |
161 | 132 | </Stack> |
162 | 133 | ) |
|
0 commit comments