-
Notifications
You must be signed in to change notification settings - Fork 23
Feature/max one click transfer #1799
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
base: v2
Are you sure you want to change the base?
Changes from all commits
2e256a7
af68a8c
2294404
e798fb8
6c8e6a5
b84997b
17bae23
bf6c1cc
36fa2fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,7 +33,7 @@ import { | |
| import wait from '../../utils/wait' | ||
| import { EstimationStatus } from '../estimation/types' | ||
| import EventEmitter from '../eventEmitter/eventEmitter' | ||
| import { SignAccountOpController } from '../signAccountOp/signAccountOp' | ||
| import { SignAccountOpController, SigningStatus } from '../signAccountOp/signAccountOp' | ||
|
|
||
| const CONVERSION_PRECISION = 16 | ||
| const CONVERSION_PRECISION_POW = BigInt(10 ** CONVERSION_PRECISION) | ||
|
|
@@ -80,6 +80,15 @@ export class TransferController extends EventEmitter implements ITransferControl | |
| */ | ||
| amount = '' | ||
|
|
||
| /** | ||
| * The final amount to send. Another property is needed so we could | ||
| * differentiate between the amount the user wants to send and the | ||
| * amount he could actually send. This is in case the user selected MAX | ||
| * but doesn't have anything left to pay the fee with | ||
| * Sanitized | ||
| */ | ||
| finalAmount: bigint = 0n | ||
|
|
||
| amountInFiat = '' | ||
|
|
||
| /** | ||
|
|
@@ -220,15 +229,21 @@ export class TransferController extends EventEmitter implements ITransferControl | |
| this.#selectedToken = token | ||
|
|
||
| if ( | ||
| prevSelectedToken?.address !== token?.address || | ||
| prevSelectedToken?.chainId !== token?.chainId | ||
| prevSelectedToken?.address !== token.address || | ||
| prevSelectedToken?.chainId !== token.chainId | ||
| ) { | ||
| if (!token.priceIn.length) { | ||
| this.amountFieldMode = 'token' | ||
| } | ||
| this.#setAmountAndNotifyUI('') | ||
| this.#setAmountInFiatAndNotifyUI('') | ||
| this.#setSWWarningVisibleIfNeeded() | ||
|
|
||
| // upon an update of the selected token, update the accountOp meta | ||
| if (this.signAccountOpController?.accountOp.meta?.adjustableAccountOpMode) { | ||
| this.signAccountOpController.accountOp.meta.adjustableAccountOpMode.tokenAddress = | ||
| token.address | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -349,6 +364,74 @@ export class TransferController extends EventEmitter implements ITransferControl | |
| return this.addressState.ensAddress || this.addressState.fieldValue | ||
| } | ||
|
|
||
| /** | ||
| * Adjust the amount and fee | ||
| */ | ||
| async adjustTransferAmountAndFee() { | ||
| const op = this.signAccountOpController?.accountOp | ||
| if ( | ||
| !op?.gasFeePayment || | ||
| !this.#selectedToken || | ||
| this.signAccountOpController?.status?.type !== SigningStatus.ReadyToSign | ||
| ) { | ||
| this.emitUpdate() | ||
| return | ||
| } | ||
|
|
||
| this.finalAmount = parseUnits( | ||
| getSanitizedAmount(this.amount, this.#selectedToken.decimals), | ||
| this.#selectedToken.decimals | ||
| ) | ||
| if ( | ||
| this.signAccountOpController.feeTokenResult!.address === this.#selectedToken.address && | ||
| this.signAccountOpController.selectedOption!.paidBy === | ||
| this.signAccountOpController.accountOp.accountAddr | ||
| ) { | ||
| // check if we're sending max amount | ||
| // if we are, reduce it minus estimated fee and update the calls | ||
| const currentPortfolio = this.#portfolio.getLatestPortfolioState(op.accountAddr) | ||
| const currentPortfolioNetwork = currentPortfolio[op.chainId.toString()] | ||
| const portfolioToken = currentPortfolioNetwork?.result?.tokens.find( | ||
| (token) => token.address === this.#selectedToken!.address | ||
| ) | ||
| // @justInCase | ||
| if (portfolioToken) { | ||
| // is max? | ||
| if (portfolioToken.amount === this.finalAmount) { | ||
| this.finalAmount -= op.gasFeePayment.amount | ||
| const userRequest = buildTransferUserRequest({ | ||
| selectedAccount: op.accountAddr, | ||
| amount: formatUnits(this.finalAmount, this.#selectedToken.decimals), | ||
| selectedToken: this.#selectedToken, | ||
| recipientAddress: this.isTopUp | ||
| ? FEE_COLLECTOR | ||
| : getAddressFromAddressState(this.addressState) | ||
| }) | ||
|
|
||
| // @justInCase | ||
| if (userRequest && userRequest.action.kind === 'calls') { | ||
| this.signAccountOpController.accountOp.calls = userRequest.action.calls | ||
| } | ||
| } | ||
| } | ||
| } else { | ||
| const userRequest = buildTransferUserRequest({ | ||
| selectedAccount: op.accountAddr, | ||
| amount: formatUnits(this.finalAmount, this.#selectedToken.decimals), | ||
| selectedToken: this.#selectedToken, | ||
| recipientAddress: this.isTopUp | ||
| ? FEE_COLLECTOR | ||
| : getAddressFromAddressState(this.addressState) | ||
| }) | ||
| // @justInCase | ||
| if (userRequest && userRequest.action.kind === 'calls') { | ||
| this.signAccountOpController.accountOp.calls = userRequest.action.calls | ||
| } | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need the else logic?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're updating the calls depending on the chosen fee token. If the first case is "send max USDT and pay in USDT", we will perform the "if" calculation. But if the user then changes to another fee option instead, we enter the "else" and allow the user to send the full max USDT he has. Otherwise, he would have sent the subtracted value |
||
|
|
||
| this.emitUpdate() | ||
| } | ||
|
|
||
| async update({ | ||
| humanizerInfo, | ||
| selectedToken, | ||
|
|
@@ -620,7 +703,7 @@ export class TransferController extends EventEmitter implements ITransferControl | |
| network | ||
| ) | ||
|
|
||
| const accountOp = { | ||
| const accountOp: AccountOp = { | ||
| accountAddr: this.#selectedAccountData.account.addr, | ||
| chainId: network.chainId, | ||
| signingKeyAddr: null, | ||
|
|
@@ -631,7 +714,10 @@ export class TransferController extends EventEmitter implements ITransferControl | |
| signature: null, | ||
| calls, | ||
| meta: { | ||
| paymasterService: getAmbirePaymasterService(baseAcc, this.#relayerUrl) | ||
| paymasterService: getAmbirePaymasterService(baseAcc, this.#relayerUrl), | ||
| adjustableAccountOpMode: { | ||
| tokenAddress: this.#selectedToken!.address | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -656,7 +742,8 @@ export class TransferController extends EventEmitter implements ITransferControl | |
| // propagate updates from signAccountOp here | ||
| this.#signAccountOpSubscriptions.push( | ||
| this.signAccountOpController.onUpdate(() => { | ||
| this.emitUpdate() | ||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
| this.adjustTransferAmountAndFee() | ||
| }) | ||
| ) | ||
| this.#signAccountOpSubscriptions.push( | ||
|
|
@@ -667,6 +754,7 @@ export class TransferController extends EventEmitter implements ITransferControl | |
| }) | ||
| ) | ||
|
|
||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
| this.reestimate() | ||
| } | ||
|
|
||
|
|
@@ -694,6 +782,7 @@ export class TransferController extends EventEmitter implements ITransferControl | |
| } | ||
|
|
||
| if (this.signAccountOpController?.estimation.errors.length) { | ||
| // eslint-disable-next-line no-console | ||
| console.log( | ||
| 'Errors on Transfer re-estimate', | ||
| this.signAccountOpController.estimation.errors | ||
|
|
@@ -702,6 +791,7 @@ export class TransferController extends EventEmitter implements ITransferControl | |
| } | ||
| } | ||
|
|
||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
| loop() | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.