|
| 1 | +import { useCallback, useEffect, useState } from 'react'; |
| 2 | +import { parseUnits, toHex, type Hex } from 'viem'; |
| 3 | +import { StyledForm } from './styles'; |
| 4 | +import type { ERC20TokenStreamPermissionRequest } from './types'; |
| 5 | + |
| 6 | +type ERC20TokenStreamFormProps = { |
| 7 | + onChange: (request: ERC20TokenStreamPermissionRequest) => void; |
| 8 | +}; |
| 9 | + |
| 10 | +export const ERC20TokenStreamForm = ({ |
| 11 | + onChange, |
| 12 | +}: ERC20TokenStreamFormProps) => { |
| 13 | + const [initialAmount, setInitialAmount] = useState<bigint | null>( |
| 14 | + BigInt(toHex(parseUnits('.5', 18))), |
| 15 | + ); |
| 16 | + const [amountPerSecond, setAmountPerSecond] = useState( |
| 17 | + BigInt(toHex(parseUnits('.5', 18))), |
| 18 | + ); |
| 19 | + const [maxAmount, setMaxAmount] = useState<bigint | null>( |
| 20 | + BigInt(toHex(parseUnits('2.5', 18))), |
| 21 | + ); |
| 22 | + const [startTime, setStartTime] = useState(Math.floor(Date.now() / 1000)); |
| 23 | + const [expiry, setExpiry] = useState( |
| 24 | + Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 30, // 30 days from now |
| 25 | + ); |
| 26 | + const [justification, setJustification] = useState( |
| 27 | + 'This is a very important request for streaming allowance for some very important thing', |
| 28 | + ); |
| 29 | + const [isAdjustmentAllowed, setIsAdjustmentAllowed] = useState(true); |
| 30 | + const [tokenAddress, setTokenAddress] = useState<Hex>( |
| 31 | + '0x616553f076c6f66739a99fef373c6b4ae1b22a7a', // Consensys USDC |
| 32 | + ); |
| 33 | + |
| 34 | + const handleInitialAmountChange = useCallback( |
| 35 | + ({ |
| 36 | + target: { value: inputValue }, |
| 37 | + }: React.ChangeEvent<HTMLInputElement>) => { |
| 38 | + if (inputValue.trim() === '') { |
| 39 | + setInitialAmount(null); |
| 40 | + } else { |
| 41 | + setInitialAmount(BigInt(inputValue)); |
| 42 | + } |
| 43 | + }, |
| 44 | + [], |
| 45 | + ); |
| 46 | + |
| 47 | + const handleAmountPerSecondChange = useCallback( |
| 48 | + ({ |
| 49 | + target: { value: inputValue }, |
| 50 | + }: React.ChangeEvent<HTMLInputElement>) => { |
| 51 | + setAmountPerSecond(BigInt(inputValue)); |
| 52 | + }, |
| 53 | + [], |
| 54 | + ); |
| 55 | + |
| 56 | + const handleMaxAmountChange = useCallback( |
| 57 | + ({ |
| 58 | + target: { value: inputValue }, |
| 59 | + }: React.ChangeEvent<HTMLInputElement>) => { |
| 60 | + if (inputValue.trim() === '') { |
| 61 | + setMaxAmount(null); |
| 62 | + } else { |
| 63 | + setMaxAmount(BigInt(inputValue)); |
| 64 | + } |
| 65 | + }, |
| 66 | + [], |
| 67 | + ); |
| 68 | + |
| 69 | + const handleStartTimeChange = useCallback( |
| 70 | + ({ |
| 71 | + target: { value: inputValue }, |
| 72 | + }: React.ChangeEvent<HTMLInputElement>) => { |
| 73 | + setStartTime(Number(inputValue)); |
| 74 | + }, |
| 75 | + [], |
| 76 | + ); |
| 77 | + |
| 78 | + const handleJustificationChange = useCallback( |
| 79 | + ({ |
| 80 | + target: { value: inputValue }, |
| 81 | + }: React.ChangeEvent<HTMLTextAreaElement>) => { |
| 82 | + setJustification(inputValue); |
| 83 | + }, |
| 84 | + [], |
| 85 | + ); |
| 86 | + |
| 87 | + const handleExpiryChange = useCallback( |
| 88 | + ({ |
| 89 | + target: { value: inputValue }, |
| 90 | + }: React.ChangeEvent<HTMLInputElement>) => { |
| 91 | + setExpiry(Number(inputValue)); |
| 92 | + }, |
| 93 | + [], |
| 94 | + ); |
| 95 | + |
| 96 | + const handleIsAdjustmentAllowedChange = useCallback( |
| 97 | + ({ target: { checked } }: React.ChangeEvent<HTMLInputElement>) => { |
| 98 | + setIsAdjustmentAllowed(checked); |
| 99 | + }, |
| 100 | + [], |
| 101 | + ); |
| 102 | + |
| 103 | + const handleTokenAddressChange = useCallback( |
| 104 | + ({ |
| 105 | + target: { value: inputValue }, |
| 106 | + }: React.ChangeEvent<HTMLInputElement>) => { |
| 107 | + setTokenAddress(inputValue as Hex); |
| 108 | + }, |
| 109 | + [], |
| 110 | + ); |
| 111 | + |
| 112 | + useEffect(() => { |
| 113 | + onChange({ |
| 114 | + type: 'erc20-token-stream', |
| 115 | + initialAmount: initialAmount ? toHex(initialAmount) : null, |
| 116 | + amountPerSecond: toHex(amountPerSecond), |
| 117 | + maxAmount: maxAmount ? toHex(maxAmount) : null, |
| 118 | + startTime, |
| 119 | + expiry, |
| 120 | + justification, |
| 121 | + isAdjustmentAllowed, |
| 122 | + tokenAddress, |
| 123 | + }); |
| 124 | + }, [ |
| 125 | + onChange, |
| 126 | + initialAmount, |
| 127 | + amountPerSecond, |
| 128 | + maxAmount, |
| 129 | + startTime, |
| 130 | + expiry, |
| 131 | + justification, |
| 132 | + isAdjustmentAllowed, |
| 133 | + tokenAddress, |
| 134 | + ]); |
| 135 | + |
| 136 | + return ( |
| 137 | + <StyledForm> |
| 138 | + <div> |
| 139 | + <label htmlFor="tokenAddress">Token Address:</label> |
| 140 | + <input |
| 141 | + type="text" |
| 142 | + id="tokenAddress" |
| 143 | + name="tokenAddress" |
| 144 | + value={tokenAddress} |
| 145 | + onChange={handleTokenAddressChange} |
| 146 | + placeholder="0x..." |
| 147 | + /> |
| 148 | + </div> |
| 149 | + <div> |
| 150 | + <label htmlFor="initialAmount">Initial Amount:</label> |
| 151 | + <input |
| 152 | + type="text" |
| 153 | + id="initialAmount" |
| 154 | + name="initialAmount" |
| 155 | + value={initialAmount?.toString()} |
| 156 | + onChange={handleInitialAmountChange} |
| 157 | + /> |
| 158 | + </div> |
| 159 | + <div> |
| 160 | + <label htmlFor="amountPerSecond">Amount Per Second:</label> |
| 161 | + <input |
| 162 | + type="text" |
| 163 | + id="amountPerSecond" |
| 164 | + name="amountPerSecond" |
| 165 | + value={amountPerSecond.toString()} |
| 166 | + onChange={handleAmountPerSecondChange} |
| 167 | + /> |
| 168 | + </div> |
| 169 | + <div> |
| 170 | + <label htmlFor="maxAmount">Max Amount:</label> |
| 171 | + <input |
| 172 | + type="text" |
| 173 | + id="maxAmount" |
| 174 | + name="maxAmount" |
| 175 | + value={maxAmount?.toString()} |
| 176 | + onChange={handleMaxAmountChange} |
| 177 | + /> |
| 178 | + </div> |
| 179 | + <div> |
| 180 | + <label htmlFor="startTime">Start Time:</label> |
| 181 | + <input |
| 182 | + type="number" |
| 183 | + id="startTime" |
| 184 | + name="startTime" |
| 185 | + value={startTime} |
| 186 | + onChange={handleStartTimeChange} |
| 187 | + /> |
| 188 | + </div> |
| 189 | + <div> |
| 190 | + <label htmlFor="justification">Justification:</label> |
| 191 | + <textarea |
| 192 | + id="justification" |
| 193 | + name="justification" |
| 194 | + rows={3} |
| 195 | + value={justification} |
| 196 | + onChange={handleJustificationChange} |
| 197 | + ></textarea> |
| 198 | + </div> |
| 199 | + <div> |
| 200 | + <label htmlFor="expiry">Expiry:</label> |
| 201 | + <input |
| 202 | + type="number" |
| 203 | + id="expiry" |
| 204 | + name="expiry" |
| 205 | + value={expiry} |
| 206 | + onChange={handleExpiryChange} |
| 207 | + /> |
| 208 | + </div> |
| 209 | + <div style={{ display: 'flex', alignItems: 'center' }}> |
| 210 | + <label htmlFor="isAdjustmentAllowed">Allow Adjustments:</label> |
| 211 | + <input |
| 212 | + type="checkbox" |
| 213 | + id="isAdjustmentAllowed" |
| 214 | + name="isAdjustmentAllowed" |
| 215 | + checked={isAdjustmentAllowed} |
| 216 | + onChange={handleIsAdjustmentAllowedChange} |
| 217 | + style={{ width: 'auto', marginLeft: '1rem' }} |
| 218 | + /> |
| 219 | + </div> |
| 220 | + </StyledForm> |
| 221 | + ); |
| 222 | +}; |
0 commit comments