5
5
Heading ,
6
6
PlusSVG ,
7
7
Spinner ,
8
+ Toast ,
8
9
Typography ,
9
10
} from '@ensdomains/thorin'
10
11
import { useEffect , useState } from 'react'
@@ -40,6 +41,9 @@ export function Manage() {
40
41
const delegationInfo = useDelegationInfo ( address )
41
42
const [ isSearchModalOpen , setIsSearchModalOpen ] = useState ( false )
42
43
const [ isConfirmationModalOpen , setIsConfirmationModalOpen ] = useState ( false )
44
+ const [ delegationType , setDelegationType ] = useState < 'native' | 'multi' > (
45
+ 'multi'
46
+ )
43
47
44
48
const [ delegates , setDelegates ] = useState < DelegateSelection > ( new Map ( ) )
45
49
const delegatesArr = Array . from ( delegates )
@@ -123,7 +127,8 @@ export function Manage() {
123
127
useEffect ( ( ) => {
124
128
// Refetch the delegateInfo 1s after a transaction (to let the indexer catch up)
125
129
if ( receipt . status ) {
126
- setTimeout ( ( ) => delegationInfo . refetch ( ) , 1000 )
130
+ setIsConfirmationModalOpen ( false )
131
+ delegationInfo . refetch ( )
127
132
}
128
133
// eslint-disable-next-line react-hooks/exhaustive-deps
129
134
} , [ receipt . status ] )
@@ -133,11 +138,24 @@ export function Manage() {
133
138
navigate ( '/strategy' )
134
139
}
135
140
141
+ function handleNativeDelegate ( ) {
142
+ if ( ! address ) return
143
+
144
+ // We will only be here if allocatedDelegates.length === 1, so it's safe to use the first
145
+ write . writeContract ( {
146
+ ...ensTokenContract ,
147
+ functionName : 'delegate' ,
148
+ args : [ allocatedDelegates [ 0 ] [ 0 ] ] ,
149
+ } )
150
+ }
151
+
136
152
function handleMultiDelegate ( ) {
137
153
if ( ! address ) return
138
154
139
155
if ( allocatedDelegates . map ( ( del ) => del [ 0 ] ) . includes ( address ) ) {
140
- return alert ( 'You cannot delegate to yourself' )
156
+ return alert (
157
+ 'You cannot delegate to yourself via the multi-delegate contract.'
158
+ )
141
159
}
142
160
143
161
const positiveChangingDelegates = changingDelegates
@@ -283,19 +301,15 @@ export function Manage() {
283
301
284
302
console . log ( { sources, targets, amounts } )
285
303
286
- write
287
- . writeContractAsync ( {
288
- ...erc20MultiDelegateContract ,
289
- functionName : 'delegateMulti' ,
290
- args : [
291
- sources . map ( ( address ) => BigInt ( address ) ) , // sources[]
292
- targets . map ( ( address ) => BigInt ( address ) ) , // targets[]
293
- amounts , // amounts[]
294
- ] ,
295
- } )
296
- . catch ( ( e ) => {
297
- console . error ( e )
298
- } )
304
+ write . writeContract ( {
305
+ ...erc20MultiDelegateContract ,
306
+ functionName : 'delegateMulti' ,
307
+ args : [
308
+ sources . map ( ( address ) => BigInt ( address ) ) , // sources[]
309
+ targets . map ( ( address ) => BigInt ( address ) ) , // targets[]
310
+ amounts , // amounts[]
311
+ ] ,
312
+ } )
299
313
}
300
314
301
315
return (
@@ -364,50 +378,53 @@ export function Manage() {
364
378
prefix = { < PlusSVG /> }
365
379
onClick = { ( ) => setIsSearchModalOpen ( true ) }
366
380
>
367
- Add delegate
381
+ { multiDelegates ?. length === 0 && allocatedDelegates . length === 0
382
+ ? 'Add or change delegate'
383
+ : 'Add delegate' }
368
384
</ Button >
369
-
370
- { /* <Button
371
- prefix={<PlusSVG />}
372
- onClick={() => {
373
- write.writeContract({
374
- ...ensTokenContract,
375
- functionName: 'delegate',
376
- args: [address!],
377
- })
378
- }}
379
- >
380
- Delegate all tokens natively
381
- </Button> */ }
382
385
</ ButtonWrapper >
383
386
384
- { receipt . isSuccess && (
385
- < Helper type = "success" className = "mx-auto" >
386
- Transaction success!
387
- </ Helper >
388
- ) }
387
+ { /* receipt.isSuccess */ }
388
+ < Toast
389
+ open = { receipt . isSuccess }
390
+ title = "Transaction success!"
391
+ description = "Your transaction has been confirmed."
392
+ variant = "desktop"
393
+ onClose = { ( ) => write . reset ( ) }
394
+ msToShow = { 7000 }
395
+ >
396
+ < Button
397
+ as = "a"
398
+ target = "_blank"
399
+ href = { `https://etherscan.io/tx/${ write . data } ` }
400
+ colorStyle = "bluePrimary"
401
+ >
402
+ View on Etherscan
403
+ </ Button >
404
+ </ Toast >
389
405
390
406
{ receipt . isLoading && (
391
407
< Spinner size = "medium" color = "blue" className = "mx-auto" />
392
408
) }
393
409
394
- { receipt . isError && (
395
- < Helper type = "error" >
396
- < div >
397
- Transaction failed. It will likely work if you try again a few
398
- times. Tenderly sends a different gas estimate to the wallet each
399
- time for some reason.{ ' ' }
400
- < a
401
- href = "https://dashboard.tenderly.co/explorer/vnet/78d3d569-cb63-45a9-8b8c-9d152d90c3ed/transactions"
402
- target = "_blank"
403
- className = "text-ens-red-primary font-bold underline"
404
- >
405
- See more here
406
- </ a >
407
- .
408
- </ div >
409
- </ Helper >
410
- ) }
410
+ { /* receipt.isError */ }
411
+ < Toast
412
+ open = { receipt . isError }
413
+ title = "Transaction failed!"
414
+ description = "Your transaction has failed."
415
+ variant = "desktop"
416
+ onClose = { ( ) => write . reset ( ) }
417
+ msToShow = { 7000 }
418
+ >
419
+ < Button
420
+ as = "a"
421
+ target = "_blank"
422
+ href = { `https://etherscan.io/tx/${ write . data } ` }
423
+ colorStyle = "redPrimary"
424
+ >
425
+ View on Etherscan
426
+ </ Button >
427
+ </ Toast >
411
428
412
429
< ButtonWrapper >
413
430
{ ( ( ) => {
@@ -472,6 +489,8 @@ export function Manage() {
472
489
473
490
// If there's exactly one delegate AND the allocated amount is moast of the balance, present the option of native delegation
474
491
if (
492
+ // We can only show this if there are no existing multi-delegates, otherwise we'd need a multi-step process to reclaim tokens first
493
+ ( multiDelegates ?? [ ] ) . length === 0 &&
475
494
allocatedDelegates . length === 1 &&
476
495
allocatedDelegates [ 0 ] [ 1 ] . newBalance > almostFullBalance
477
496
) {
@@ -481,7 +500,7 @@ export function Manage() {
481
500
// Radio options to select native or multi-delegate
482
501
return (
483
502
< div className = "flex w-[28rem] max-w-full flex-col gap-2" >
484
- < label htmlFor = "native " className = { optionsClassName } >
503
+ < label htmlFor = "multi " className = { optionsClassName } >
485
504
< Typography asProp = "p" >
486
505
Delegate a portion, swapping your $ENS for NFTs that
487
506
represent each delegate. You can undelegate anytime to swap
@@ -492,12 +511,13 @@ export function Manage() {
492
511
type = "radio"
493
512
className = "appearance-auto"
494
513
name = "delegation-type"
495
- id = "native "
514
+ id = "multi "
496
515
defaultChecked
516
+ onChange = { ( ) => setDelegationType ( 'multi' ) }
497
517
/>
498
518
</ label >
499
519
500
- < label htmlFor = "multi " className = { optionsClassName } >
520
+ < label htmlFor = "native " className = { optionsClassName } >
501
521
< Typography asProp = "p" >
502
522
Delegate all your $ENS to one person, including any new $ENS
503
523
you receive, for less gas.
@@ -507,7 +527,8 @@ export function Manage() {
507
527
type = "radio"
508
528
className = "appearance-auto"
509
529
name = "delegation-type"
510
- id = "multi"
530
+ id = "native"
531
+ onChange = { ( ) => setDelegationType ( 'native' ) }
511
532
/>
512
533
</ label >
513
534
</ div >
@@ -537,8 +558,14 @@ export function Manage() {
537
558
trailing = {
538
559
< Button
539
560
colorStyle = "bluePrimary"
540
- onClick = { handleMultiDelegate }
541
- loading = { receipt . isLoading }
561
+ onClick = { ( ) => {
562
+ if ( delegationType === 'native' ) {
563
+ handleNativeDelegate ( )
564
+ } else {
565
+ handleMultiDelegate ( )
566
+ }
567
+ } }
568
+ loading = { receipt . isLoading || write . isPending }
542
569
disabled = { toBeAllocated < 0n || changingDelegates . length === 0 }
543
570
>
544
571
Open Wallet
0 commit comments