Skip to content

Commit

Permalink
Merge pull request #549 from nimiq/usdt
Browse files Browse the repository at this point in the history
Add support for swapping USDT
  • Loading branch information
sisou authored Nov 5, 2024
2 parents 640967f + 25c9f71 commit 99859a3
Show file tree
Hide file tree
Showing 12 changed files with 207 additions and 136 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
.DS_Store
node_modules
dist
client/dist
client/build

# only yarn
package-lock.json
Expand Down
2 changes: 0 additions & 2 deletions client/.gitignore

This file was deleted.

28 changes: 23 additions & 5 deletions client/PublicRequestTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,14 +271,22 @@ export interface BitcoinHtlcCreationInstructions {
}

export interface PolygonHtlcCreationInstructions extends RelayRequest {
type: 'USDC_MATIC';
type: 'USDC_MATIC' | 'USDT_MATIC';
/**
* The sender's nonce in the token contract, required when calling the
* contract function `openWithPermit`.
*/
permit?: {
tokenNonce: number,
};

/**
* The sender's nonce in the token contract, required when calling the
* contract function `openWithApproval`.
*/
approval?: {
tokenNonce: number,
};
}

export interface EuroHtlcCreationInstructions {
Expand Down Expand Up @@ -312,7 +320,7 @@ export interface BitcoinHtlcSettlementInstructions {
}

export interface PolygonHtlcSettlementInstructions extends RelayRequest {
type: 'USDC_MATIC';
type: 'USDC_MATIC' | 'USDT_MATIC';
amount: number;
}

Expand Down Expand Up @@ -360,8 +368,12 @@ export interface BitcoinHtlcRefundInstructions {
}

export interface PolygonHtlcRefundInstructions extends RelayRequest {
type: 'USDC_MATIC' | 'USDC';
type: 'USDC_MATIC' | 'USDC' | 'USDT_MATIC';
amount: number;
/**
* The token contract address. Required for calling the bridged HTLC contract.
*/
token: string;
}

export type HtlcCreationInstructions =
Expand Down Expand Up @@ -411,6 +423,7 @@ export interface SetupSwapRequest extends SimpleRequest {
polygonAddresses?: Array<{
address: string,
usdcBalance: number, // In USDC's smallest unit
usdtBalance: number, // In USDT's smallest unit
}>;

// Optional KYC info for swapping at higher limits
Expand All @@ -427,6 +440,7 @@ export interface SetupSwapResult {
nimProxy?: SignedTransaction;
btc?: SignedBtcTransaction;
usdc?: SignedPolygonTransaction;
usdt?: SignedPolygonTransaction;
eur?: string; // When funding EUR: empty string, when redeeming EUR: JWS of the settlement instructions
refundTx?: string;
}
Expand Down Expand Up @@ -612,7 +626,7 @@ export interface SignPolygonTransactionRequest extends BasicRequest, RelayReques
recipientLabel?: string;
/**
* The sender's nonce in the token contract, required when calling the
* contract function `swapWithApproval` for bridged USDC.e.
* contract function `swapWithApproval` for bridged USDC.e and `transferWithApproval` for bridged USDT.
*/
approval?: {
tokenNonce: number,
Expand All @@ -626,7 +640,7 @@ export interface SignPolygonTransactionRequest extends BasicRequest, RelayReques
};

/**
* The amount of USDC to transfer. Required when calling the contract
* The amount of USDC/T to transfer. Required when calling the contract
* methods 'redeem' and 'redeemWithSecretInData' for HTLCs.
*/
amount?: number;
Expand All @@ -635,6 +649,10 @@ export interface SignPolygonTransactionRequest extends BasicRequest, RelayReques
* methods 'redeem' and 'redeemWithSecretInData' for HTLCs.
*/
senderLabel?: string;
/**
* The token contract address. Required for calling the bridged HTLC contract.
*/
token?: string;
}

export interface SignedPolygonTransaction {
Expand Down
6 changes: 3 additions & 3 deletions client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nimiq/hub-api",
"version": "1.8.0",
"version": "1.9.0",
"main": "dist/HubApi.umd.js",
"module": "dist/HubApi.es.js",
"repository": "https://github.com/nimiq/hub/tree/master/client",
Expand All @@ -9,8 +9,8 @@
"private": false,
"types": "types/index.d.ts",
"dependencies": {
"@nimiq/core-web": "^1.6.1",
"@nimiq/fastspot-api": "^1.8.0",
"@nimiq/core-web": "^1.6.3",
"@nimiq/fastspot-api": "^1.10.2",
"@nimiq/rpc": "^0.4.0",
"@nimiq/utils": "^0.5.0",
"@opengsn/common": "^2.2.5",
Expand Down
18 changes: 9 additions & 9 deletions client/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -227,15 +227,15 @@
"@ethersproject/properties" "^5.7.0"
"@ethersproject/strings" "^5.7.0"

"@nimiq/core-web@^1.6.1":
version "1.6.1"
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.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/core-web@^1.6.3":
version "1.6.3"
resolved "https://registry.yarnpkg.com/@nimiq/core-web/-/core-web-1.6.3.tgz#b4a8f8c5d289850b20cb4009766af5b1cafd6e20"
integrity sha512-D6RrJi2cRU81odNpmwczhUBvOQ47+/Db1svrTkH/G4xNd72lr9MS5nMdfpUz+rBRnSprljrzW2mdUtZ6W9bPaA==

"@nimiq/fastspot-api@^1.10.2":
version "1.10.2"
resolved "https://registry.yarnpkg.com/@nimiq/fastspot-api/-/fastspot-api-1.10.2.tgz#ae2cbe5b41359875bece9ce0957209ca1b486d21"
integrity sha512-rPy3DhWlqTOj4k9/YMS2mizjR1rLsQzqWzMnJ+xDEGprgb6/jhDBZs/CW+jONccRQWrvuyBYTrrxdLgn3imKjQ==

"@nimiq/rpc@^0.4.0":
version "0.4.0"
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
"dependencies": {
"@nimiq/browser-warning": "^1.1.1",
"@nimiq/electrum-client": "https://github.com/nimiq/electrum-client#build",
"@nimiq/fastspot-api": "^1.8.0",
"@nimiq/fastspot-api": "^1.10.2",
"@nimiq/iqons": "^1.5.2",
"@nimiq/keyguard-client": "^1.6.0",
"@nimiq/keyguard-client": "^1.7.1",
"@nimiq/ledger-api": "^3.0.0",
"@nimiq/network-client": "^0.6.2",
"@nimiq/oasis-api": "^1.1.1",
Expand All @@ -44,8 +44,8 @@
"vuex-class": "^0.3.2"
},
"devDependencies": {
"@nimiq/core": "^1.6.1",
"@nimiq/core-web": "^1.6.1",
"@nimiq/core": "^1.6.3",
"@nimiq/core-web": "^1.6.3",
"@vue/cli-plugin-babel": "~4.5.19",
"@vue/cli-plugin-typescript": "~4.5.19",
"@vue/cli-plugin-unit-jest": "~4.5.19",
Expand Down
98 changes: 58 additions & 40 deletions src/lib/RequestParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -527,18 +527,25 @@ export class RequestParser {

// Validate and parse only what we use in the Hub

if (!['NIM', 'BTC', 'USDC_MATIC', 'EUR'].includes(setupSwapRequest.fund.type)) {
if (!['NIM', 'BTC', 'USDC_MATIC', 'USDT_MATIC', 'EUR'].includes(setupSwapRequest.fund.type)) {
throw new Error('Funding type is not supported');
}

if (!['NIM', 'BTC', 'USDC_MATIC', 'EUR'].includes(setupSwapRequest.redeem.type)) {
if (!['NIM', 'BTC', 'USDC_MATIC', 'USDT_MATIC', 'EUR'].includes(setupSwapRequest.redeem.type)) {
throw new Error('Redeeming type is not supported');
}

if (setupSwapRequest.fund.type === setupSwapRequest.redeem.type) {
throw new Error('Cannot swap between the same types');
}

if (
(setupSwapRequest.fund.type === 'USDC_MATIC' && setupSwapRequest.redeem.type === 'USDT_MATIC')
|| (setupSwapRequest.fund.type === 'USDT_MATIC' && setupSwapRequest.redeem.type === 'USDC_MATIC')
) {
throw new Error('Cannot swap between USDC and USDT');
}

if (setupSwapRequest.layout === 'slider') {
if (
typeof setupSwapRequest.direction !== 'string'
Expand Down Expand Up @@ -573,13 +580,15 @@ export class RequestParser {
}

const polygonAddress = setupSwapRequest.fund.type === SwapAsset.USDC_MATIC
|| setupSwapRequest.fund.type === SwapAsset.USDT_MATIC
? setupSwapRequest.fund.request.from
: setupSwapRequest.redeem.type === SwapAsset.USDC_MATIC
|| setupSwapRequest.redeem.type === SwapAsset.USDT_MATIC
? setupSwapRequest.redeem.request.from
: undefined;
if (polygonAddress && !setupSwapRequest.polygonAddresses.some(
({ address }) => address === polygonAddress)) {
throw new Error('The address details of the USDC address doing the swap must be provided');
throw new Error('The address details of the Polgyon address doing the swap must be provided');
}
}

Expand Down Expand Up @@ -616,44 +625,53 @@ export class RequestParser {
}
}

const fund: ParsedSetupSwapRequest['fund'] | null = setupSwapRequest.fund.type === 'NIM' ? {
...setupSwapRequest.fund,
type: SwapAsset[setupSwapRequest.fund.type],
sender: Nimiq.Address.fromAny(setupSwapRequest.fund.sender),
} : setupSwapRequest.fund.type === 'BTC' ? {
...setupSwapRequest.fund,
type: SwapAsset[setupSwapRequest.fund.type],
} : setupSwapRequest.fund.type === 'USDC_MATIC' || setupSwapRequest.fund.type === 'USDT_MATIC' ? {
...setupSwapRequest.fund,
type: SwapAsset[setupSwapRequest.fund.type],
} : setupSwapRequest.fund.type === 'EUR' ? {
...setupSwapRequest.fund,
type: SwapAsset[setupSwapRequest.fund.type],
} : null;

if (!fund) {
throw new Error('Unsupported funding object type');
}

const redeem: ParsedSetupSwapRequest['redeem'] | null = setupSwapRequest.redeem.type === 'NIM' ? {
...setupSwapRequest.redeem,
type: SwapAsset[setupSwapRequest.redeem.type],
recipient: Nimiq.Address.fromAny(setupSwapRequest.redeem.recipient),
extraData: typeof setupSwapRequest.redeem.extraData === 'string'
? Nimiq.BufferUtils.fromAny(setupSwapRequest.redeem.extraData)
: setupSwapRequest.redeem.extraData,
} : setupSwapRequest.redeem.type === 'BTC' ? {
...setupSwapRequest.redeem,
type: SwapAsset[setupSwapRequest.redeem.type],
} : setupSwapRequest.redeem.type === 'USDC_MATIC' || setupSwapRequest.redeem.type === 'USDT_MATIC' ? {
...setupSwapRequest.redeem,
type: SwapAsset[setupSwapRequest.redeem.type],
} : setupSwapRequest.redeem.type === 'EUR' ? {
...setupSwapRequest.redeem,
type: SwapAsset[setupSwapRequest.redeem.type],
} : null;

if (!redeem) {
throw new Error('Unsupported redeeming object type');
}

const parsedSetupSwapRequest: ParsedSetupSwapRequest = {
kind: RequestType.SETUP_SWAP,
walletId: setupSwapRequest.accountId,
...setupSwapRequest,

fund: setupSwapRequest.fund.type === 'NIM' ? {
...setupSwapRequest.fund,
type: SwapAsset[setupSwapRequest.fund.type],
sender: Nimiq.Address.fromAny(setupSwapRequest.fund.sender),
} : setupSwapRequest.fund.type === 'BTC' ? {
...setupSwapRequest.fund,
type: SwapAsset[setupSwapRequest.fund.type],
} : setupSwapRequest.fund.type === 'USDC_MATIC' ? {
...setupSwapRequest.fund,
type: SwapAsset[setupSwapRequest.fund.type],
} : { // EUR
...setupSwapRequest.fund,
type: SwapAsset[setupSwapRequest.fund.type],
},

redeem: setupSwapRequest.redeem.type === 'NIM' ? {
...setupSwapRequest.redeem,
type: SwapAsset[setupSwapRequest.redeem.type],
recipient: Nimiq.Address.fromAny(setupSwapRequest.redeem.recipient),
extraData: typeof setupSwapRequest.redeem.extraData === 'string'
? Nimiq.BufferUtils.fromAny(setupSwapRequest.redeem.extraData)
: setupSwapRequest.redeem.extraData,
} : setupSwapRequest.redeem.type === 'BTC' ? {
...setupSwapRequest.redeem,
type: SwapAsset[setupSwapRequest.redeem.type],
} : setupSwapRequest.redeem.type === 'USDC_MATIC' ? {
...setupSwapRequest.redeem,
type: SwapAsset[setupSwapRequest.redeem.type],
} : { // EUR
...setupSwapRequest.redeem,
type: SwapAsset[setupSwapRequest.redeem.type],
},

fund,
redeem,
layout: setupSwapRequest.layout || 'standard',
};

Expand All @@ -664,8 +682,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', 'USDC_MATIC'].includes(refundSwapRequest.refund.type)) {
throw new Error('Refunding object type must be "NIM", "BTC", "USDC", or "USDC_MATIC"');
if (!['NIM', 'BTC', 'USDC', 'USDC_MATIC', 'USDT_MATIC'].includes(refundSwapRequest.refund.type)) {
throw new Error('Refunding object type must be "NIM", "BTC", "USDC", "USDC_MATIC" or "USDT_MATIC"');
}

const parsedRefundSwapRequest: ParsedRefundSwapRequest = {
Expand All @@ -684,7 +702,7 @@ export class RequestParser {
} : refundSwapRequest.refund.type === 'BTC' ? {
...refundSwapRequest.refund,
type: SwapAsset[refundSwapRequest.refund.type],
} : { // USDC
} : { // USDC/T
...refundSwapRequest.refund,
type: SwapAsset[refundSwapRequest.refund.type],
},
Expand Down
12 changes: 9 additions & 3 deletions src/lib/RequestTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export interface ParsedSignPolygonTransactionRequest extends ParsedBasicRequest,
};
amount?: number;
senderLabel?: string;
token?: string;
}

/**
Expand Down Expand Up @@ -200,10 +201,13 @@ export interface ParsedSetupSwapRequest extends ParsedSimpleRequest {
// htlcScript: Uint8Array,
refundAddress: string,
} | ({
type: SwapAsset.USDC_MATIC,
type: SwapAsset.USDC_MATIC | SwapAsset.USDT_MATIC,
permit?: {
tokenNonce: number,
},
approval?: {
tokenNonce: number,
},
} & RelayRequest) | {
type: SwapAsset.EUR,
value: number, // Eurocents
Expand Down Expand Up @@ -234,7 +238,7 @@ export interface ParsedSetupSwapRequest extends ParsedSimpleRequest {
value: number, // Sats
};
} | ({
type: SwapAsset.USDC_MATIC,
type: SwapAsset.USDC_MATIC | SwapAsset.USDT_MATIC,
amount: number,
} & RelayRequest) | {
type: SwapAsset.EUR,
Expand Down Expand Up @@ -278,6 +282,7 @@ export interface ParsedSetupSwapRequest extends ParsedSimpleRequest {
polygonAddresses?: Array<{
address: string,
usdcBalance: number, // In USDC's smallest unit
usdtBalance: number, // In USDT's smallest unit
}>;

// Optional KYC info for swapping at higher limits
Expand Down Expand Up @@ -313,8 +318,9 @@ export interface ParsedRefundSwapRequest extends ParsedSimpleRequest {
};
refundAddress: string; // My address, must be refund address of HTLC
} | ({
type: SwapAsset.USDC_MATIC | SwapAsset.USDC,
type: SwapAsset.USDC_MATIC | SwapAsset.USDC | SwapAsset.USDT_MATIC,
amount: number,
token: string,
} & RelayRequest);
}

Expand Down
Loading

0 comments on commit 99859a3

Please sign in to comment.