From 35eb80133b3610fc9fc5928cb8ab1fc8e210a592 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=B6ren?= <hello@soerenschwert.de>
Date: Mon, 4 Mar 2024 13:52:39 +0100
Subject: [PATCH] Allow native USDC `openWithPermit` function

And switch to `SwapAsset.USDC_MATIC`.
---
 client/PublicRequestTypes.ts    | 10 +++++-----
 client/package.json             |  2 +-
 client/yarn.lock                |  8 ++++----
 package.json                    |  4 ++--
 src/lib/RequestParser.ts        | 24 ++++++++++++------------
 src/lib/RequestTypes.ts         |  8 ++++----
 src/views/RefundSwap.vue        |  2 +-
 src/views/RefundSwapSuccess.vue |  5 ++++-
 src/views/SetupSwap.vue         | 11 +++++------
 src/views/SetupSwapSuccess.vue  | 24 ++++++++++++------------
 yarn.lock                       | 16 ++++++++--------
 11 files changed, 58 insertions(+), 56 deletions(-)

diff --git a/client/PublicRequestTypes.ts b/client/PublicRequestTypes.ts
index 554f9315..a7cde705 100644
--- a/client/PublicRequestTypes.ts
+++ b/client/PublicRequestTypes.ts
@@ -271,12 +271,12 @@ export interface BitcoinHtlcCreationInstructions {
 }
 
 export interface PolygonHtlcCreationInstructions extends RelayRequest {
-    type: 'USDC';
+    type: 'USDC_MATIC';
     /**
      * The sender's nonce in the token contract, required when calling the
-     * contract function `openWithApproval`.
+     * contract function `openWithPermit`.
      */
-    approval?: {
+    permit?: {
         tokenNonce: number,
     };
 }
@@ -312,7 +312,7 @@ export interface BitcoinHtlcSettlementInstructions {
 }
 
 export interface PolygonHtlcSettlementInstructions extends RelayRequest {
-    type: 'USDC';
+    type: 'USDC_MATIC';
     amount: number;
 }
 
@@ -360,7 +360,7 @@ export interface BitcoinHtlcRefundInstructions {
 }
 
 export interface PolygonHtlcRefundInstructions extends RelayRequest {
-    type: 'USDC';
+    type: 'USDC_MATIC' | 'USDC';
     amount: number;
 }
 
diff --git a/client/package.json b/client/package.json
index 7538fffb..65a9823d 100644
--- a/client/package.json
+++ b/client/package.json
@@ -10,7 +10,7 @@
   "types": "types/index.d.ts",
   "dependencies": {
     "@nimiq/core-web": "^1.6.1",
-    "@nimiq/fastspot-api": "^1.7.0",
+    "@nimiq/fastspot-api": "^1.8.0",
     "@nimiq/rpc": "^0.4.0",
     "@nimiq/utils": "^0.5.0",
     "@opengsn/common": "^2.2.5",
diff --git a/client/yarn.lock b/client/yarn.lock
index 58fc2b5e..cd1a205c 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -232,10 +232,10 @@
   resolved "https://registry.yarnpkg.com/@nimiq/core-web/-/core-web-1.6.1.tgz#97cb5b43b257c7f6f6808ef603e9bf686377241f"
   integrity sha512-WYw2brIxUXa/SQ0JRp0RXWQKzBFhROXrEjF9Eh+tRlC+NrI2ObwRQkwJCbP2qmPtYldIimfyECmsDVHFoyLXjQ==
 
-"@nimiq/fastspot-api@^1.7.0":
-  version "1.7.0"
-  resolved "https://registry.yarnpkg.com/@nimiq/fastspot-api/-/fastspot-api-1.7.0.tgz#eea496f4898139c51971f0b177583c6cc6b06611"
-  integrity sha512-k7vSjYdjAiAQar+21v681Zrp9kN9uHMUw7/dyG2D/IrXqNPHkD133JyrzxPBga+myW411SUR4mtdPQRPQm3d/A==
+"@nimiq/fastspot-api@^1.8.0":
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/@nimiq/fastspot-api/-/fastspot-api-1.8.0.tgz#705a9e79e425c3e6536d8994fd0b39d88af1b268"
+  integrity sha512-qNkibJnxS8ndOn4tuy1m3lSNKybBYApo+wy1ajTKcQ0lHo3VfLY0sAJ+WRE7diVWCa7iumu6wsFVudyc3k8/NQ==
 
 "@nimiq/rpc@^0.4.0":
   version "0.4.0"
diff --git a/package.json b/package.json
index 44faf1d5..3cf60d9d 100644
--- a/package.json
+++ b/package.json
@@ -21,9 +21,9 @@
     "dependencies": {
         "@nimiq/browser-warning": "^1.1.1",
         "@nimiq/electrum-client": "https://github.com/nimiq/electrum-client#build",
-        "@nimiq/fastspot-api": "^1.7.0",
+        "@nimiq/fastspot-api": "^1.8.0",
         "@nimiq/iqons": "^1.5.2",
-        "@nimiq/keyguard-client": "^1.5.4",
+        "@nimiq/keyguard-client": "^1.6.0",
         "@nimiq/ledger-api": "^2.3.0",
         "@nimiq/network-client": "^0.6.2",
         "@nimiq/oasis-api": "^1.1.1",
diff --git a/src/lib/RequestParser.ts b/src/lib/RequestParser.ts
index 509cee2f..c5ea0577 100644
--- a/src/lib/RequestParser.ts
+++ b/src/lib/RequestParser.ts
@@ -527,11 +527,11 @@ export class RequestParser {
 
                 // Validate and parse only what we use in the Hub
 
-                if (!['NIM', 'BTC', 'USDC', 'EUR'].includes(setupSwapRequest.fund.type)) {
+                if (!['NIM', 'BTC', 'USDC_MATIC', 'EUR'].includes(setupSwapRequest.fund.type)) {
                     throw new Error('Funding type is not supported');
                 }
 
-                if (!['NIM', 'BTC', 'USDC', 'EUR'].includes(setupSwapRequest.redeem.type)) {
+                if (!['NIM', 'BTC', 'USDC_MATIC', 'EUR'].includes(setupSwapRequest.redeem.type)) {
                     throw new Error('Redeeming type is not supported');
                 }
 
@@ -562,9 +562,9 @@ export class RequestParser {
                         throw new Error('When using the "slider" layout, `bitcoinAccount` must be provided');
                     }
 
-                    const nimiqAddress = setupSwapRequest.fund.type === 'NIM'
+                    const nimiqAddress = setupSwapRequest.fund.type === SwapAsset.NIM
                         ? Nimiq.Address.fromAny(setupSwapRequest.fund.sender)
-                        : setupSwapRequest.redeem.type === 'NIM'
+                        : setupSwapRequest.redeem.type === SwapAsset.NIM
                             ? Nimiq.Address.fromAny(setupSwapRequest.redeem.recipient)
                             : undefined;
                     if (nimiqAddress && !setupSwapRequest.nimiqAddresses.some(
@@ -572,9 +572,9 @@ export class RequestParser {
                         throw new Error('The address details of the NIM address doing the swap must be provided');
                     }
 
-                    const polygonAddress = setupSwapRequest.fund.type === 'USDC'
+                    const polygonAddress = setupSwapRequest.fund.type === SwapAsset.USDC_MATIC
                         ? setupSwapRequest.fund.request.from
-                        : setupSwapRequest.redeem.type === 'USDC'
+                        : setupSwapRequest.redeem.type === SwapAsset.USDC_MATIC
                             ? setupSwapRequest.redeem.request.from
                             : undefined;
                     if (polygonAddress && !setupSwapRequest.polygonAddresses.some(
@@ -583,7 +583,7 @@ export class RequestParser {
                     }
                 }
 
-                if (setupSwapRequest.redeem.type === 'NIM') {
+                if (setupSwapRequest.redeem.type === SwapAsset.NIM) {
                     if (!setupSwapRequest.redeem.validityStartHeight
                         || setupSwapRequest.redeem.validityStartHeight < 1) {
                         throw new Error(
@@ -592,7 +592,7 @@ export class RequestParser {
                     }
                 }
 
-                if (setupSwapRequest.fund.type === 'NIM') {
+                if (setupSwapRequest.fund.type === SwapAsset.NIM) {
                     if (!setupSwapRequest.fund.validityStartHeight
                         || setupSwapRequest.fund.validityStartHeight < 1) {
                         throw new Error(`Invalid validity start height: ${setupSwapRequest.fund.validityStartHeight}`);
@@ -628,7 +628,7 @@ export class RequestParser {
                     } : setupSwapRequest.fund.type === 'BTC' ? {
                         ...setupSwapRequest.fund,
                         type: SwapAsset[setupSwapRequest.fund.type],
-                    } : setupSwapRequest.fund.type === 'USDC' ? {
+                    } : setupSwapRequest.fund.type === 'USDC_MATIC' ? {
                         ...setupSwapRequest.fund,
                         type: SwapAsset[setupSwapRequest.fund.type],
                     } : { // EUR
@@ -646,7 +646,7 @@ export class RequestParser {
                     } : setupSwapRequest.redeem.type === 'BTC' ? {
                         ...setupSwapRequest.redeem,
                         type: SwapAsset[setupSwapRequest.redeem.type],
-                    } : setupSwapRequest.redeem.type === 'USDC' ? {
+                    } : setupSwapRequest.redeem.type === 'USDC_MATIC' ? {
                         ...setupSwapRequest.redeem,
                         type: SwapAsset[setupSwapRequest.redeem.type],
                     } : { // EUR
@@ -664,8 +664,8 @@ export class RequestParser {
                 // Only basic parsing and validation. Refund transaction specific data will be validated by the Keyguard
                 // or subsequent Ledger transaction signing requests.
 
-                if (!['NIM', 'BTC', 'USDC'].includes(refundSwapRequest.refund.type)) {
-                    throw new Error('Refunding object type must be "NIM", "BTC", or "USDC"');
+                if (!['NIM', 'BTC', 'USDC', 'USDC_MATIC'].includes(refundSwapRequest.refund.type)) {
+                    throw new Error('Refunding object type must be "NIM", "BTC", "USDC", or "USDC_MATIC"');
                 }
 
                 const parsedRefundSwapRequest: ParsedRefundSwapRequest = {
diff --git a/src/lib/RequestTypes.ts b/src/lib/RequestTypes.ts
index 7fa425da..ada6919c 100644
--- a/src/lib/RequestTypes.ts
+++ b/src/lib/RequestTypes.ts
@@ -198,8 +198,8 @@ export interface ParsedSetupSwapRequest extends ParsedSimpleRequest {
         // htlcScript: Uint8Array,
         refundAddress: string,
     } | ({
-        type: SwapAsset.USDC,
-        approval?: {
+        type: SwapAsset.USDC_MATIC,
+        permit?: {
             tokenNonce: number,
         },
     } & RelayRequest) | {
@@ -232,7 +232,7 @@ export interface ParsedSetupSwapRequest extends ParsedSimpleRequest {
             value: number, // Sats
         };
     } | ({
-        type: SwapAsset.USDC,
+        type: SwapAsset.USDC_MATIC,
         amount: number,
     } & RelayRequest) | {
         type: SwapAsset.EUR,
@@ -311,7 +311,7 @@ export interface ParsedRefundSwapRequest extends ParsedSimpleRequest {
         };
         refundAddress: string; // My address, must be refund address of HTLC
     } | ({
-        type: SwapAsset.USDC,
+        type: SwapAsset.USDC_MATIC | SwapAsset.USDC,
         amount: number,
     } & RelayRequest);
 }
diff --git a/src/views/RefundSwap.vue b/src/views/RefundSwap.vue
index e91697cc..b1db757a 100644
--- a/src/views/RefundSwap.vue
+++ b/src/views/RefundSwap.vue
@@ -122,7 +122,7 @@ export default class RefundSwap extends BitcoinSyncBaseView {
             this._signTransaction(signRequest);
         }
 
-        if (refundInfo.type === SwapAsset.USDC) {
+        if (refundInfo.type === SwapAsset.USDC_MATIC || refundInfo.type === SwapAsset.USDC) {
             const signer = account.polygonAddresses.find((ai) => ai.address === refundInfo.request.from);
 
             if (!signer) {
diff --git a/src/views/RefundSwapSuccess.vue b/src/views/RefundSwapSuccess.vue
index 13f22709..9244a03c 100644
--- a/src/views/RefundSwapSuccess.vue
+++ b/src/views/RefundSwapSuccess.vue
@@ -55,7 +55,10 @@ export default class SignBtcTransactionSuccess extends Vue {
             return;
         }
 
-        if ('message' in this.keyguardResult && this.request.refund.type === SwapAsset.USDC) {
+        if (
+            'message' in this.keyguardResult
+            && (this.request.refund.type === SwapAsset.USDC_MATIC || this.request.refund.type === SwapAsset.USDC)
+        ) {
             const result: SignedPolygonTransaction = {
                 message: this.keyguardResult.message,
                 signature: this.keyguardResult.signature,
diff --git a/src/views/SetupSwap.vue b/src/views/SetupSwap.vue
index 949bffbd..ba698a40 100644
--- a/src/views/SetupSwap.vue
+++ b/src/views/SetupSwap.vue
@@ -11,7 +11,6 @@ import { WalletInfo } from '../lib/WalletInfo';
 import { BtcAddressInfo } from '../lib/bitcoin/BtcAddressInfo';
 import { SwapAsset } from '@nimiq/fastspot-api';
 import { DEFAULT_KEY_PATH } from '../lib/Constants';
-import { threadId } from 'worker_threads';
 
 // Import only types to avoid bundling of KeyguardClient in Ledger request if not required.
 // (But note that currently, the KeyguardClient is still always bundled in the RpcApi).
@@ -183,7 +182,7 @@ export default class SetupSwap extends BitcoinSyncBaseView {
             };
         }
 
-        if (this.request.fund.type === SwapAsset.USDC) {
+        if (this.request.fund.type === SwapAsset.USDC_MATIC) {
             const senderAddress = this.request.fund.request.from;
             const signer = this._account.polygonAddresses.find((ai) => ai.address === senderAddress);
 
@@ -192,11 +191,11 @@ export default class SetupSwap extends BitcoinSyncBaseView {
             }
 
             fundingInfo = {
-                type: SwapAsset.USDC,
+                type: SwapAsset.USDC_MATIC,
                 keyPath: signer.path,
                 request: this.request.fund.request,
                 relayData: this.request.fund.relayData,
-                approval: this.request.fund.approval,
+                permit: this.request.fund.permit,
             };
         }
 
@@ -255,7 +254,7 @@ export default class SetupSwap extends BitcoinSyncBaseView {
             };
         }
 
-        if (this.request.redeem.type === SwapAsset.USDC) {
+        if (this.request.redeem.type === SwapAsset.USDC_MATIC) {
             const senderAddress = this.request.redeem.request.from;
             const signer = this._account.polygonAddresses.find((ai) => ai.address === senderAddress);
 
@@ -264,7 +263,7 @@ export default class SetupSwap extends BitcoinSyncBaseView {
             }
 
             redeemingInfo = {
-                type: SwapAsset.USDC,
+                type: SwapAsset.USDC_MATIC,
                 keyPath: signer.path,
                 request: this.request.redeem.request,
                 relayData: this.request.redeem.relayData,
diff --git a/src/views/SetupSwapSuccess.vue b/src/views/SetupSwapSuccess.vue
index 890e6b2b..a75b0b1a 100644
--- a/src/views/SetupSwapSuccess.vue
+++ b/src/views/SetupSwapSuccess.vue
@@ -84,7 +84,7 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
             case SwapAsset.BTC:
                 refundAddress = this.request.fund.refundAddress;
                 break;
-            case SwapAsset.USDC:
+            case SwapAsset.USDC_MATIC:
                 refundAddress = this.request.fund.request.from;
                 break;
             default: break;
@@ -98,7 +98,7 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
             case SwapAsset.BTC:
                 redeemAddress = this.request.redeem.output.address;
                 break;
-            case SwapAsset.USDC:
+            case SwapAsset.USDC_MATIC:
                 redeemAddress = this.request.redeem.request.from;
                 break;
             case SwapAsset.EUR:
@@ -228,8 +228,8 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
             }
         }
 
-        if (confirmedSwap.from.asset === SwapAsset.USDC || confirmedSwap.to.asset === SwapAsset.USDC) {
-            const contract = confirmedSwap.contracts[SwapAsset.USDC] as Contract<SwapAsset.USDC>;
+        if (confirmedSwap.from.asset === SwapAsset.USDC_MATIC || confirmedSwap.to.asset === SwapAsset.USDC_MATIC) {
+            const contract = confirmedSwap.contracts[SwapAsset.USDC_MATIC] as Contract<SwapAsset.USDC_MATIC>;
             const htlc = contract.htlc as UsdcHtlcDetails;
 
             const contractData = {
@@ -254,11 +254,11 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
             }
             hashRoot = contractData.hash;
 
-            if (confirmedSwap.from.asset === SwapAsset.USDC && refundAddress !== contractData.refundAddress) {
+            if (confirmedSwap.from.asset === SwapAsset.USDC_MATIC && refundAddress !== contractData.refundAddress) {
                 this.$rpc.reject(new Error('Unknown HTLC refund address'));
                 return;
             }
-            if (confirmedSwap.to.asset === SwapAsset.USDC && redeemAddress !== contractData.recipientAddress) {
+            if (confirmedSwap.to.asset === SwapAsset.USDC_MATIC && redeemAddress !== contractData.recipientAddress) {
                 this.$rpc.reject(new Error('Unknown HTLC redeem address'));
                 return;
             }
@@ -304,8 +304,8 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
             };
         }
 
-        if (this.request.fund.type === SwapAsset.USDC) {
-            const usdcHtlcData = confirmedSwap.contracts[SwapAsset.USDC]!.htlc as UsdcHtlcDetails;
+        if (this.request.fund.type === SwapAsset.USDC_MATIC) {
+            const usdcHtlcData = confirmedSwap.contracts[SwapAsset.USDC_MATIC]!.htlc as UsdcHtlcDetails;
 
             if (!usdcHtlcData.data) {
                 // TODO: Create data with ethersJS
@@ -313,7 +313,7 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
             }
 
             fundingHtlcInfo = {
-                type: SwapAsset.USDC,
+                type: SwapAsset.USDC_MATIC,
                 htlcData: usdcHtlcData.data,
             };
         }
@@ -409,12 +409,12 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
             }
         }
 
-        if (this.request.redeem.type === SwapAsset.USDC) {
-            const usdcContract = confirmedSwap.contracts[SwapAsset.USDC] as Contract<SwapAsset.USDC>;
+        if (this.request.redeem.type === SwapAsset.USDC_MATIC) {
+            const usdcContract = confirmedSwap.contracts[SwapAsset.USDC_MATIC] as Contract<SwapAsset.USDC_MATIC>;
             const usdcHtlcData = usdcContract.htlc as UsdcHtlcDetails;
 
             redeemingHtlcInfo = {
-                type: SwapAsset.USDC,
+                type: SwapAsset.USDC_MATIC,
                 hash: confirmedSwap.hash,
                 timeout: usdcContract.timeout,
                 htlcId: usdcHtlcData.address,
diff --git a/yarn.lock b/yarn.lock
index 886ddbcf..4428cec4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1520,10 +1520,10 @@
   dependencies:
     bitcoinjs-lib "^5.1.10"
 
-"@nimiq/fastspot-api@^1.7.0":
-  version "1.7.0"
-  resolved "https://registry.yarnpkg.com/@nimiq/fastspot-api/-/fastspot-api-1.7.0.tgz#eea496f4898139c51971f0b177583c6cc6b06611"
-  integrity sha512-k7vSjYdjAiAQar+21v681Zrp9kN9uHMUw7/dyG2D/IrXqNPHkD133JyrzxPBga+myW411SUR4mtdPQRPQm3d/A==
+"@nimiq/fastspot-api@^1.8.0":
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/@nimiq/fastspot-api/-/fastspot-api-1.8.0.tgz#705a9e79e425c3e6536d8994fd0b39d88af1b268"
+  integrity sha512-qNkibJnxS8ndOn4tuy1m3lSNKybBYApo+wy1ajTKcQ0lHo3VfLY0sAJ+WRE7diVWCa7iumu6wsFVudyc3k8/NQ==
 
 "@nimiq/iqons@^1.5.2", "@nimiq/iqons@^1.6.0":
   version "1.6.0"
@@ -1541,10 +1541,10 @@
     btoa "^1.1.2"
     node-lmdb "^0.9.6"
 
-"@nimiq/keyguard-client@^1.5.4":
-  version "1.5.4"
-  resolved "https://registry.yarnpkg.com/@nimiq/keyguard-client/-/keyguard-client-1.5.4.tgz#0842386e1c36e2b203cae94ac62ffafd48ee79fa"
-  integrity sha512-E4sQys66nSPCiMRm2NN9ibm0RZCA9VbyP7UApSEBShBCHkCCSYp1zjfuPm9+v4VKwA7yw+pEDZ5RE7Mlf2OdIQ==
+"@nimiq/keyguard-client@^1.6.0":
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/@nimiq/keyguard-client/-/keyguard-client-1.6.0.tgz#e2cb22c1af5ae68dac74e5ead9e69aeaba2ffc8b"
+  integrity sha512-DDi+PycBtiTJO5Jwk3mHZzBP2FHARNCIR+9C6+uhw6kRlT/pcDS+vE6GeTGOwZsRGOtowLG3d9n43xWoPOOcGQ==
   dependencies:
     "@nimiq/core-web" "1.5.8"
     "@nimiq/rpc" "^0.3.0"