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

feat: Mass payouts #5042

Open
wants to merge 17 commits into
base: dev
Choose a base branch
from
Open

feat: Mass payouts #5042

wants to merge 17 commits into from

Conversation

tmjssz
Copy link
Collaborator

@tmjssz tmjssz commented Feb 19, 2025

What it solves

Resolves #4895 and #2332

Enhances the token transfer functionality by introducing support for multiple recipients.

How this PR fixes it

  • Users can now add up to 5 recipients per transaction, each with different tokens and amounts.
  • This functionality is currently limited to standard transactions - spending limits remain restricted to a single recipient.

Targeted rollout

The feature is behind the TARGETED_MASS_PAYOUTS feature flag and will only be available for specific Safe addresses (outreachId = 4).

UI & validation improvements

  • Enhanced validation logic to support multi-recipient transfers and improved UI feedback for validation errors.
  • Updated recipient management with an “+ Add Recipient” button and a dynamic counter.
  • New alerts for user guidance:
    • Info alert with a link to the CSV Airdrop app when multiple recipients are added (if active on the network).
    • Warning alert when the max limit 5 five recipients is reached.
    • Error alert when the total token amount exceeds the available balance.

Code changes

  • Transaction flow updates (CreateTokenTransfer.tsx)
    • Implemented multi-recipient support with updated validation and UI.
    • Added CSV airdrop modal when recipient limit is reached.
  • Token input validation (TokenAmountInput/index.tsx)
    • Improved validation logic for multi-recipient support.
    • Updated UI to handle and display validation errors correctly.
  • Minor fixes

How to test it

  1. Ensure your Safe is targeted for the feature (see Notion page). The feature must be enabled and Safe address targeted for outreachId = 4.
  2. Open the “Send Tokens” flow.
  3. Verify that the “+ Add Recipient” button appears and increments the counter correctly.
  4. If CSV Airdrop is enabled for the network: Check that the info alert with a link to the app appears when adding at least one additional recipient.
  5. Verify that adding the limit of five recipients triggers a warning alert.
  6. Ensure that when the total transfer amount exceeds the balance of a token, an error alert and field validation errors appear.

Screenshots

Screenshot 2025-02-19 at 18 31 28

Screenshot 2025-02-19 at 18 32 22

Screenshot 2025-02-19 at 18 32 45

Screenshot 2025-02-19 at 18 33 34

mass-payouts.mp4

Checklist

  • I've tested the branch on mobile 📱
  • I've documented how it affects the analytics (if at all) 📊
  • I've written a unit/e2e test for it (if applicable) 🧑‍💻

Copy link

github-actions bot commented Feb 19, 2025

@tmjssz tmjssz requested review from katspaugh and iamacook February 19, 2025 17:40
Copy link

github-actions bot commented Feb 19, 2025

📦 Next.js Bundle Analysis for @safe-global/web

This analysis was generated by the Next.js Bundle Analysis action. 🤖

⚠️ Global Bundle Size Increased

Page Size (compressed)
global 1.07 MB (🟡 +84.15 KB)
Details

The global bundle is the javascript bundle that loads alongside every page. It is in its own category because its impact is much higher - an increase to its size means that every page on your website loads slower, and a decrease means every page loads faster.

Any third party scripts you have added directly to your app using the <script> tag are not accounted for in this analysis

If you want further insight into what is behind the changes, give @next/bundle-analyzer a try!

Thirty-five Pages Changed Size

The following pages changed size from the code in this PR compared to its base branch:

Page Size (compressed) First Load
/ 509 B (🟢 -1 B) 1.07 MB
/address-book 23.22 KB (🟡 +155 B) 1.09 MB
/apps 35.75 KB (🟡 +2.03 KB) 1.11 MB
/apps/custom 33.82 KB (🟡 +2.03 KB) 1.1 MB
/apps/open 55.36 KB (🟡 +1.75 KB) 1.13 MB
/balances 29.73 KB (🟡 +25 B) 1.1 MB
/balances/nfts 9.32 KB (🟢 -231 B) 1.08 MB
/bridge 2.55 KB (🟢 -3 B) 1.07 MB
/cookie 8.77 KB (🟡 +1 B) 1.08 MB
/home 61.35 KB (🟡 +2.09 KB) 1.13 MB
/licenses 2.46 KB (🟢 -1 B) 1.07 MB
/new-safe/advanced-create 26.38 KB (🟢 -77 B) 1.1 MB
/new-safe/create 25.52 KB (🟢 -70 B) 1.1 MB
/privacy 14.57 KB (🟡 +2 B) 1.09 MB
/settings/appearance 2.25 KB (🟡 +1 B) 1.07 MB
/settings/cookies 1.87 KB (🟡 +1 B) 1.07 MB
/settings/data 1.79 KB (🟢 -1 B) 1.07 MB
/settings/environment-variables 3.28 KB (🟡 +1 B) 1.07 MB
/settings/modules 2.88 KB (🟢 -1.18 KB) 1.07 MB
/settings/notifications 7.49 KB (🟢 -13.82 KB) 1.08 MB
/settings/safe-apps 20.15 KB (🟡 +1.87 KB) 1.09 MB
/settings/security 2.34 KB (🟡 +2 B) 1.07 MB
/settings/setup 30.98 KB (🟡 +256 B) 1.1 MB
/share/safe-app 7.55 KB (🟢 -9 B) 1.08 MB
/stake 617 B (🟢 -2 B) 1.07 MB
/swap 763 B (🟡 +3 B) 1.07 MB
/terms 12.93 KB (🟡 +1 B) 1.08 MB
/transactions 99.46 KB (🟡 +2.88 KB) 1.17 MB
/transactions/history 99.42 KB (🟡 +2.88 KB) 1.17 MB
/transactions/messages 60.25 KB (🟡 +1.95 KB) 1.13 MB
/transactions/msg 56.5 KB (🟡 +1.95 KB) 1.13 MB
/transactions/queue 49.36 KB (🟡 +1.96 KB) 1.12 MB
/transactions/tx 48.72 KB (🟡 +1.96 KB) 1.12 MB
/welcome 6.93 KB (🟢 -2 B) 1.08 MB
/welcome/accounts 408 B (🟡 +1 B) 1.07 MB
Details

Only the gzipped size is provided here based on an expert tip.

First Load is the size of the global bundle plus the bundle for the individual page. If a user were to show up to your website and land on a given page, the first load size represents the amount of javascript that user would need to download. If next/link is used, subsequent page loads would only need to download that page's bundle (the number in the "Size" column), since the global bundle has already been downloaded.

Any third party scripts you have added directly to your app using the <script> tag are not accounted for in this analysis

Next to the size is how much the size has increased or decreased compared with the base branch of this PR. If this percentage has increased by 20% or more, there will be a red status indicator applied, indicating that special attention should be given to this.

Copy link

github-actions bot commented Feb 19, 2025

Coverage report for apps/web

St.
Category Percentage Covered / Total
🟡 Statements
77.26% (-0.06% 🔻)
14312/18524
🔴 Branches
55.98% (-0.16% 🔻)
3581/6397
🟡 Functions
62.07% (-0.21% 🔻)
2134/3438
🟡 Lines
78.74% (-0.05% 🔻)
12929/16420
Show new covered files 🐣
St.
File Statements Branches Functions Lines
🟢
... / index.tsx
92% 57.14% 80% 95.65%
🟢
... / index.tsx
80% 0% 0% 88.89%
🟡
... / ReviewRecipientRow.tsx
56.25% 0% 0% 64.29%
Show files with reduced coverage 🔻
St.
File Statements Branches Functions Lines
🟢
... / useRemoteSafeApps.ts
86.84% (-4.07% 🔻)
57.14% (-9.52% 🔻)
83.33% (-7.58% 🔻)
90.63% (-2.23% 🔻)
🟡
... / index.tsx
75%
33.33% (-16.67% 🔻)
20% (-5% 🔻)
75% (-1.47% 🔻)
🟢
... / CreateTokenTransfer.tsx
80% (-17.62% 🔻)
50% (-26.19% 🔻)
50% (-50% 🔻)
80.65% (-19.35% 🔻)
🟡
... / index.tsx
62.22% (-13.78% 🔻)
40.91% (-12.42% 🔻)
42.86% (+2.86% 🔼)
64.29% (-13.98% 🔻)
🔴
... / ReviewTokenTransfer.tsx
42.31% (-1.69% 🔻)
0% 0%
47.83% (-2.17% 🔻)

Test suite run success

1839 tests passing in 251 suites.

Report generated by 🧪jest coverage report action from 4c28a60


export enum TokenAmountFields {
tokenAddress = 'tokenAddress',
amount = 'amount',
}

export const InsufficientFundsValidationError = 'Insufficient funds'
Copy link
Member

Choose a reason for hiding this comment

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

Nit: whilst this works, RHF errors have a type that seems more suitable for referencing. What do you think?

Copy link
Collaborator Author

@tmjssz tmjssz Feb 21, 2025

Choose a reason for hiding this comment

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

Not sure I understand how you would change it. Do you mean the FieldError type? How would you use it?
This string is basically just used as a return value for the validateLimitedAmount function and then to check whether any such error exists in the form to conditionally render the error alert below it. Happy to refactor but just not sure how :)

Copy link
Member

Choose a reason for hiding this comment

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

We can keep the constant as it's used in all places but, yes, FieldError['type'] is more explicit. I'm not sure on the feasibility so I'll leave it up to your discretion.

}

// Validate the total amount of the selected token in the multi transfer
const recipients = getValues(MultiTokenTransferFields.recipients) as MultiTokenTransferParams['recipients']
Copy link
Member

Choose a reason for hiding this comment

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

Nit: if you pass the form type as a generic to useFormContext, I think you might get the inferred types.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done. I had to add a separate useFormContext<MultiTokenTransferParams>() hook call with the passed generic type because the TokenAmountInput component is also used in CreateSpendingLimit where the form type is different (no field array). To keep the component working for both usages, the form type can not be specified for the other RFH functions.

…ormContext to preserve type of recipients values
startIcon={<SvgIcon component={AddIcon} inheritViewBox fontSize="small" />}
size="large"
>
Add Recipient
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Add Recipient
Add recipient

Here and everywhere else: please don't use Cap Case in the UI.

@IvaLukan please update the designs accordingly. Thanks!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Mass payouts] Create multi recipient flow
3 participants