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

Add support for swapping USDT #549

Merged
merged 4 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading