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(permit): modals SWAP #3158

Merged
merged 18 commits into from
Oct 5, 2023
Merged

feat(permit): modals SWAP #3158

merged 18 commits into from
Oct 5, 2023

Conversation

alfetopito
Copy link
Collaborator

@alfetopito alfetopito commented Sep 27, 2023

Summary

Fixes #3145

Add the new permit modal to SWAP (LIMIT will come in another PR)

Screen.Recording.2023-09-28.at.14.25.22.mov

It even has cosmos!

image

Notes

Styles might need some work

To Test

  1. Pick a permittable token to sell, make sure there's no allowance
  2. Using non-expert mode, confirm the swap
  • Should show the new modal in the step 1 while the permit signature is requested in the wallet
  1. Sign the approval
  • Should show the new modal in the step 2 while the order signature is requested in the wallet
  1. Sign the order
  • Should show the regular modal
  1. Place an order without permit
  • Should show the old modal
  1. Use a non-permit token that needs approval
  • Approval button should be visible
  1. Start the approval
  • Should show the old modal

@vercel
Copy link

vercel bot commented Sep 27, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
swap-dev ✅ Ready (Inspect) Visit Preview Oct 5, 2023 9:12am

🌃 Cosmos ↗︎

Comment on lines +10 to +12
const { account } = useWalletInfo()

const icon = useMemo(() => (account ? <Identicon account={account} size={80} /> : undefined), [account])
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We need a container to load the account and identicon

Comment on lines -57 to -58
'new modal + content top/bottom': (
<Wrapper>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Moved to its own file

Comment on lines -148 to -150

// New Modal to be used going forward =================================
const ModalInner = styled.div`
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Moved to its own file

Comment on lines 80 to 87
{showPermitModal(swapConfirmState) ? (
<PermitModal
onDismiss={onDismiss}
inputAmount={trade?.inputAmountWithoutFee}
outputAmount={trade?.outputAmountWithoutFee}
step={swapConfirmState?.permitSignatureState === 'signed' ? 'submit' : 'approve'}
/>
) : attemptingTxn ? (
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is the main magic!
(took me a quite awhile to figure it out :( )

permitInfo: PermitInfo | undefined
hasEnoughAllowance: boolean | undefined
permitInfo: IsTokenPermittableResult
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Simplified the parameters.
Pass undefined when hasEnoughAllowance is falsy rather than passing that property independently down

Comment on lines -19 to +21
const [swapConfirmState, setSwapConfirmState] = useAtom(swapConfirmAtom)
const setSwapConfirmState = useSetAtom(swapConfirmAtom)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Simplified this part turning into a setter only.
We don't need to read the value anywhere, and actually depending on that to update the state causes race conditions.
Replaced (where needed) with the setter taking a fn instead of the new state

Comment on lines +52 to +58
requestPermitSignature() {
setSwapConfirmState((prev) => {
const state: SwapConfirmState = { ...prev, permitSignatureState: 'requested' }
console.debug('[Swap confirm state] requestPermitSignature: ', state)
return state
})
},
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Call this only if permit is needed

Comment on lines +30 to +41
if (input.permitInfo) input.swapConfirmManager.requestPermitSignature()

input.orderParams.appData = await handlePermit({
appData: input.orderParams.appData,
hasEnoughAllowance: input.hasEnoughAllowance,

inputToken: input.context.trade.inputAmount.currency as Token,
provider: input.orderParams.signer.provider as Web3Provider,
account: input.orderParams.account,
chainId: input.orderParams.chainId,
permitInfo: input.permitInfo,
})
input.swapConfirmManager.permitSigned()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Here's the other side of the magic.
Request signature when we get to this state to show the permit modal.
Move it to next stage after signed.

@@ -12,6 +12,7 @@
"start": "nx run cowswap-frontend:serve",
"preview": "cross-env NODE_OPTIONS=--max-old-space-size=32768 nx run cowswap-frontend:preview",
"cosmos:export": "cross-env NODE_OPTIONS=--max-old-space-size=32768 nx run cowswap-frontend:cosmos:export",
"cosmos": "nx run cowswap-frontend:cosmos:run",
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Exposing it for easier access

Copy link
Contributor

Choose a reason for hiding this comment

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

I thought we had it, anyways great to add it if it wasn't

@alfetopito alfetopito marked this pull request as ready for review September 28, 2023 13:26
@alfetopito alfetopito requested a review from a team September 28, 2023 13:27
@alfetopito alfetopito changed the title feat(permit): modals feat(permit): modals SWAP Sep 28, 2023
Copy link
Contributor

@anxolin anxolin left a comment

Choose a reason for hiding this comment

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

Modal could use some styles:
image

Also, step 2, should be grayed out? (the 2 steps look active)
image

It didn't work for me in mainnet. I saw that:

  • After signing the SWAP, the order was not sent
  • The modal of confirmation showed only with the title (no content)

I'm sorry, but I didn't take a screenshot of this last thing. Because I showed you in person, I think you know what I'm talking about :)

console.debug('[Swap confirm state] permitSigned: ', state)
return state
}
console.debug('[Swap confirm state] permitSigned was a no-op')
Copy link
Contributor

Choose a reason for hiding this comment

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

what does this mean, and also, do we want to keep this debug message?

@@ -12,6 +12,7 @@
"start": "nx run cowswap-frontend:serve",
"preview": "cross-env NODE_OPTIONS=--max-old-space-size=32768 nx run cowswap-frontend:preview",
"cosmos:export": "cross-env NODE_OPTIONS=--max-old-space-size=32768 nx run cowswap-frontend:cosmos:export",
"cosmos": "nx run cowswap-frontend:cosmos:run",
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought we had it, anyways great to add it if it wasn't

@alfetopito
Copy link
Collaborator Author

It didn't work for me in mainnet. I saw that:

* After signing the SWAP, the order was not sent

* The modal of confirmation showed only with the title (no content)

I'm sorry, but I didn't take a screenshot of this last thing. Because I showed you in person, I think you know what I'm talking about :)

Irritatingly, it worked for me on mainnet for both COW and DAI.

Can you share your wallet address for me to check if there was any request against backend?

Also, can you clear the permit-* localStorage keys and try again?

@alfetopito
Copy link
Collaborator Author

It didn't work for me in mainnet. I saw that:

* After signing the SWAP, the order was not sent

* The modal of confirmation showed only with the title (no content)

I'm sorry, but I didn't take a screenshot of this last thing. Because I showed you in person, I think you know what I'm talking about :)

Irritatingly, it worked for me on mainnet for both COW and DAI.

Can you share your wallet address for me to check if there was any request against backend?

Also, can you clear the permit-* localStorage keys and try again?

Upon investigation, both order placements failed because of invalid quote ids

See the logs: https://production-6de61f.kb.eu-central-1.aws.cloud.es.io/app/r/s/MIXrT

2023-09-28T17:01:30.786Z DEBUG request{id="e9f0770e28b1d55387171503b0780d8a"}: orderbook::api::post_order: error creating order order=OrderCreation { sell_token: 0xdef1ca1fb7fbcdc777520aa7f396b4e015f497ab, buy_token: 0x6b175474e89094c44da98b954eedeac495271d0f, receiver: Some(0x79063d9173c09887d536924e2f6eadbabac099f5), sell_amount: 82434234370440077909, buy_amount: 4807141320368247242, valid_to: 1695922205, fee_amount: 215306712058039926784, kind: Sell, partially_fillable: false, sell_token_balance: Erc20, buy_token_balance: Erc20, from: Some(0x79063d9173c09887d536924e2f6eadbabac099f5), signature: Eip712("0xf491fa6c221d48c6d0e881dd6910343647dbe9a7e2c3217c96c6af98ab3428fe5e4c13f70e8eca4a35f47a7e82981495f3429b30b34a7f379aefce9f3ae087541b"), quote_id: Some(2360386), app_data: Both { full: "{\"appCode\":\"CoW Swap\",\"environment\":\"pr\",\"metadata\":{\"hooks\":{\"pre\":[{\"callData\":\"0xd505accf00000000000000000000000079063d9173c09887d536924e2f6eadbabac099f5000000000000000000000000c92e8bdf79f0507f65a392b0ab4667716bfe0110ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006e7d5877000000000000000000000000000000000000000000000000000000000000001bdd664f9cddde1506980dbcf86536c912f3f229b9a89f4956c72ef1a1225828b2136b9e9e4df50c0c3fbb8ca76f248a6ce8195bff01388e213be3a07a882307bb\",\"gasLimit\":\"80000\",\"target\":\"0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB\"}]},\"orderClass\":{\"orderClass\":\"market\"},\"quote\":{\"slippageBips\":\"50\"}},\"version\":\"0.9.0\"}", expected: 0x84a944a7dfc855dcc011f1909dc8c474732a64615218daac550bd0964012f046 } } err=OrderValidation(QuoteNotFound)
2023-09-28T16:58:34.784Z DEBUG request{id="b66c248ea6706aee46bb9958dacff048"}: orderbook::api::post_order: error creating order order=OrderCreation { sell_token: 0x6b175474e89094c44da98b954eedeac495271d0f, buy_token: 0xdef1ca1fb7fbcdc777520aa7f396b4e015f497ab, receiver: Some(0x79063d9173c09887d536924e2f6eadbabac099f5), sell_amount: 9374995690674814932, buy_amount: 159553904743365644012, valid_to: 1695922000, fee_amount: 9917473475425134592, kind: Sell, partially_fillable: false, sell_token_balance: Erc20, buy_token_balance: Erc20, from: Some(0x79063d9173c09887d536924e2f6eadbabac099f5), signature: Eip712("0x769833f6c1ecb7558b6d3d9b7196ca01787292f24abf1ca641221d318a33bff473c5ed3525e006965e3e529f626ba15d04a8c387982eeec68ef787244ef2c4d21b"), quote_id: Some(2360378), app_data: Both { full: "{\"appCode\":\"CoW Swap\",\"environment\":\"pr\",\"metadata\":{\"hooks\":{\"pre\":[{\"callData\":\"0x8fcbaf0c00000000000000000000000079063d9173c09887d536924e2f6eadbabac099f5000000000000000000000000c92e8bdf79f0507f65a392b0ab4667716bfe01100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e7d57ac0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001cf100ef7172e6cbfe7ba7c209b1fd622b7c5893a8d661bf9d52585da7de7a529f3eef6dd54625b966382106b5b05f06f1143f23427eb26c290d25b774cfa36972\",\"gasLimit\":\"80000\",\"target\":\"0x6B175474E89094C44Da98b954EedeAC495271d0F\"}]},\"orderClass\":{\"orderClass\":\"market\"},\"quote\":{\"slippageBips\":\"50\"}},\"version\":\"0.9.0\"}", expected: 0x357f97e7e6af416fe192209a50756740da609091487ab39481c5c4c674eb3cec } } err=OrderValidation(QuoteNotFound)

Searching for the quote id, it was created 2 min before

Logs: https://production-6de61f.kb.eu-central-1.aws.cloud.es.io/app/r/s/M1ojG

2023-09-28T16:56:38.874Z DEBUG request{id="8307449ab8f6f7d23cc0a7c077888a2b"}: shared::order_quoting: finished computing quote response=OrderQuoteResponse { quote: OrderQuote { sell_token: 0x6b175474e89094c44da98b954eedeac495271d0f, buy_token: 0xdef1ca1fb7fbcdc777520aa7f396b4e015f497ab, receiver: Some(0x79063d9173c09887d536924e2f6eadbabac099f5), sell_amount: 9374995690674814932, buy_amount: 160355683159161451269, valid_to: 1695921997, app_data: Both { full: "{\"appCode\":\"CoW Swap\",\"environment\":\"pr\",\"metadata\":{\"hooks\":{\"pre\":[{\"callData\":\"0x8fcbaf0c000000000000000000000000ff65d1dfcf256cf4a8d5f2fb8e70f936606b7474000000000000000000000000c92e8bdf79f0507f65a392b0ab4667716bfe01100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e7d56cc0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001b664270248c07deca65dc1c6c566e5c30cfa9d971f539ec32139d45a8dd008f1569b87cdb4b8b0c9e89c2216a959fa81405bac9842f3301f925e876c542881269\",\"gasLimit\":\"84009\",\"target\":\"0x6B175474E89094C44Da98b954EedeAC495271d0F\"}]},\"orderClass\":{\"orderClass\":\"market\"},\"quote\":{\"slippageBips\":\"50\"}},\"version\":\"0.9.0\"}", expected: 0x9053e0f991d2647f3ab05a40ebf823a052a84a0d0f1c7cf6c7f6d339c0ce78b2 }, fee_amount: 9917473475425134592, kind: Sell, partially_fillable: false, sell_token_balance: Erc20, buy_token_balance: Erc20, signing_scheme: Eip712 }, from: 0x79063d9173c09887d536924e2f6eadbabac099f5, expiration: 2023-09-28T16:57:37.540337806Z, id: Some(2360378) }

With the expiration at 2023-09-28T16:57:37.540337806Z
The order was placed at 2023-09-28T16:58:34.784Z, which is ~1min after expiration.

* You probably want to use containers/PermitModal instead
* This is the pure component for cosmos
*/
export function PermitModal(props: PermitModalProps) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Very clean and structural component!

Copy link
Collaborator

@shoom3301 shoom3301 left a comment

Choose a reason for hiding this comment

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

Works great to me!
Tested the main flow and haven't noticed any problems

@alfetopito
Copy link
Collaborator Author

FYI, the modal styled issues have been fixed by Michel in #3177 and merged to the waterfall #3164

@alfetopito alfetopito merged commit ed53662 into develop Oct 5, 2023
7 checks passed
@alfetopito alfetopito deleted the permit/3145_modals branch October 5, 2023 09:18
@github-actions github-actions bot locked and limited conversation to collaborators Oct 5, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Permit: Add modals to signing step
3 participants