Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added end/withdraw all auctions button #1630

5 changes: 5 additions & 0 deletions .changeset/short-parents-punch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'minifront': minor
---

Add a "claim all" button to the auctions list
117 changes: 117 additions & 0 deletions apps/minifront/src/components/swap/auction-list/claim-all-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { useAuctionInfos } from '../../../state/swap/dutch-auction';
import { AddressIndex } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/keys/v1/keys_pb';
import { Button } from '@repo/ui/components/ui/button';
import { AllSlices } from '../../../state';
import { useStoreShallow } from '../../../utils/use-store-shallow.ts';
import { filterWithLimit } from './helpers.ts';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@repo/ui/components/ui/tooltip';
import { AuctionInfo } from '../../../fetchers/auction-infos.ts';

const claimAllAuctionButtonSelector = (state: AllSlices) => ({
endAllAuctions: state.swap.dutchAuction.endAllAuctions,
withdrawAllAuctions: state.swap.dutchAuction.withdrawAllAuctions,
});

export interface AuctionsBatch {
auctions: AuctionInfo[];
source: AddressIndex;
}

// Assemble batch auctions for end or withdrawal.
// All auctions in the batch will have the same 'AddressIndex'
export const assembleAuctionBatch = (
auctions: AuctionInfo[],
filteredSeqNumber: bigint,
batchLimit: number,
): AuctionsBatch => {
Copy link
Contributor Author

@Valentine1898 Valentine1898 Aug 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a temporary solution that allows you to close all auctions on accounts with different batches
If a user has auctions on account 0 and account 1, the first transaction will end all auctions on account 0, and the second transaction will end all auctions on account 1

see: #1166 (comment)

const filteredBySeqAuctions: AuctionInfo[] = auctions.filter(
a => a.localSeqNum === filteredSeqNumber,
);
// Get the address index of the first auction in the list and filter other auctions with this address index
const firstFoundAddressIndex = filteredBySeqAuctions[0]?.addressIndex;

const filteredBySeqAndAddressIndexAuctions = filterWithLimit(
filteredBySeqAuctions,
a => a.addressIndex.equals(firstFoundAddressIndex),
batchLimit,
);
return { auctions: filteredBySeqAndAddressIndexAuctions, source: firstFoundAddressIndex! };
};

export const ClaimAllAuctionButton = () => {
const { endAllAuctions, withdrawAllAuctions } = useStoreShallow(claimAllAuctionButtonSelector);
const { data } = useAuctionInfos();

if (!data?.length) {
return null;
}
if (data.some(a => a.localSeqNum === 0n)) {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger
onClick={() => {
// Chain has a transaction size limit, so we can add at most a batch of 48 auctions in a single transaction
// see https://github.com/penumbra-zone/web/issues/1166#issuecomment-2263550249
const auctionBatch = assembleAuctionBatch(data, 0n, 48);

void endAllAuctions(
auctionBatch.auctions,
// TODO Should use the index of the selected account after the account selector for the auction is implemented
Copy link
Contributor Author

@Valentine1898 Valentine1898 Aug 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, we can end/withdraw all only for Account 0
see #1166 (comment)

I will open a separate issue for this to add an account selector to the auction list

UPD: I've changed the approach a bit, now users can end and withdraw auctions for all accounts, but auctions from different accounts will end or withdraw with different batches

auctionBatch.source,
);
}}
aria-label='End all open auctions, with a limit of 48 auctions per transaction'
>
<div className='w-[85px] shrink-0'>
<Button size='sm' variant='secondary' className='w-full'>
{'End all'}
Valentine1898 marked this conversation as resolved.
Show resolved Hide resolved
</Button>
</div>
</TooltipTrigger>
<TooltipContent>
End all open auctions, with a limit of 48 auctions per transaction
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}

if (data.some(a => a.localSeqNum === 1n)) {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger
onClick={() => {
// Chain has a transaction size limit, so we can add at most a batch of 48 auctions in a single transaction
// see https://github.com/penumbra-zone/web/issues/1166#issuecomment-2263550249
const auctionBatch = assembleAuctionBatch(data, 1n, 48);
void withdrawAllAuctions(
auctionBatch.auctions,
// TODO Should use the index of the selected account after the account selector for the auction is implemented
auctionBatch.source,
);
}}
aria-label='Withdraw all ended auctions, with a limit of 48 auctions per transaction'
>
<div className='w-[95px] shrink-0'>
<Button size='sm' variant='secondary' className='w-full'>
{'Withdraw all'}
Valentine1898 marked this conversation as resolved.
Show resolved Hide resolved
</Button>
</div>
</TooltipTrigger>
<TooltipContent>
Withdraw all ended auctions, with a limit of 48 auctions per transaction
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}

return null;
};
20 changes: 20 additions & 0 deletions apps/minifront/src/components/swap/auction-list/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
import { AuctionInfo } from '../../../fetchers/auction-infos';

export const filterWithLimit = <T>(
array: T[],
predicate: (value: T) => boolean,
limit: number,
): T[] => {
const result: T[] = [];
let count = 0;

for (const item of array) {
if (predicate(item)) {
result.push(item);
count++;
if (count >= limit) {
break;
}
}
}
return result;
};

export const byStartHeightAscending = (a: AuctionInfo, b: AuctionInfo) => {
if (!a.auction.description?.startHeight || !b.auction.description?.startHeight) {
return 0;
Expand Down
2 changes: 2 additions & 0 deletions apps/minifront/src/components/swap/auction-list/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useStatus } from '../../../state/status';
import { byStartHeightAscending } from './helpers';
import { Filters } from './filters';
import { AddressIndex } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/keys/v1/keys_pb.js';
import { ClaimAllAuctionButton } from './claim-all-button.tsx';

const auctionListSelector = (state: AllSlices) => ({
endAuction: state.swap.dutchAuction.endAuction,
Expand Down Expand Up @@ -66,6 +67,7 @@ export const AuctionList = () => {
{!!auctionInfos.data?.length && <QueryLatestStateButton />}

<Filters />
<ClaimAllAuctionButton />
</motion.div>
</div>

Expand Down
34 changes: 32 additions & 2 deletions apps/minifront/src/state/swap/dutch-auction/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { TransactionPlannerRequest } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb.js';
import {
TransactionPlannerRequest,
TransactionPlannerRequest_ActionDutchAuctionWithdraw,
TransactionPlannerRequest_ActionDutchAuctionEnd,
} from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb.js';
import { AllSlices, SliceCreator, useStore } from '../..';
import { planBuildBroadcast } from '../../helpers';
import { assembleScheduleRequest } from './assemble-schedule-request';
Expand Down Expand Up @@ -40,6 +44,8 @@ interface Actions {
currentSeqNum: bigint,
addressIndex: AddressIndex,
) => Promise<void>;
endAllAuctions: (auctions: AuctionInfo[], addressIndex: AddressIndex) => Promise<void>;
withdrawAllAuctions: (auctions: AuctionInfo[], addressIndex: AddressIndex) => Promise<void>;
reset: VoidFunction;
setFilter: (filter: Filter) => void;
estimate: () => Promise<void>;
Expand Down Expand Up @@ -196,7 +202,31 @@ export const createDutchAuctionSlice = (): SliceCreator<DutchAuctionSlice> => (s
await planBuildBroadcast('dutchAuctionWithdraw', req);
get().swap.dutchAuction.auctionInfos.revalidate();
},

endAllAuctions: async (auctions: AuctionInfo[], addressIndex: AddressIndex) => {
const req = new TransactionPlannerRequest({
dutchAuctionEndActions: auctions.map(
au => new TransactionPlannerRequest_ActionDutchAuctionEnd({ auctionId: au.id }),
),
source: addressIndex,
});
await planBuildBroadcast('dutchAuctionEnd', req);
get().swap.dutchAuction.auctionInfos.revalidate();
},
withdrawAllAuctions: async (auctions: AuctionInfo[], addressIndex: AddressIndex) => {
console.log(auctions);
const req = new TransactionPlannerRequest({
Valentine1898 marked this conversation as resolved.
Show resolved Hide resolved
dutchAuctionWithdrawActions: auctions.map(
au =>
new TransactionPlannerRequest_ActionDutchAuctionWithdraw({
auctionId: au.id,
seq: au.localSeqNum + 1n,
}),
),
source: addressIndex,
});
await planBuildBroadcast('dutchAuctionWithdraw', req);
get().swap.dutchAuction.auctionInfos.revalidate();
},
reset: () =>
set(({ swap }) => {
swap.dutchAuction = {
Expand Down
Loading