Skip to content

Commit

Permalink
chore: add support for crc-nim swaps
Browse files Browse the repository at this point in the history
  • Loading branch information
onmax committed Jun 19, 2024
1 parent a153f4d commit db3455e
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 15 deletions.
28 changes: 26 additions & 2 deletions client/PublicRequestTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,13 @@ export interface EuroHtlcCreationInstructions {
bankLabel?: string;
}

export interface SinpeMovilHtlcCreationInstructions {
type: 'CRC';
value: number; // CRC cents
fee: number; // CRC cents
recipientLabel?: string;
}

export interface NimiqHtlcSettlementInstructions {
type: 'NIM';
recipient: string; // My address, must be redeem address of HTLC
Expand Down Expand Up @@ -333,6 +340,20 @@ export interface EuroHtlcSettlementInstructions {
};
}

export interface SinpeMovilHtlcSettlementInstructions {
type: 'CRC';
value: number; // CRC cents
fee: number; // CRC cents
recipientLabel?: string;
settlement: {
type: 'sinpemovil',
phoneNumber: string,
// } | {
// Mock not supported yet
// type: 'mock',
};
}

export interface NimiqHtlcRefundInstructions {
type: 'NIM';
sender: string; // HTLC address
Expand Down Expand Up @@ -368,13 +389,15 @@ export type HtlcCreationInstructions =
NimiqHtlcCreationInstructions
| BitcoinHtlcCreationInstructions
| PolygonHtlcCreationInstructions
| EuroHtlcCreationInstructions;
| EuroHtlcCreationInstructions
| SinpeMovilHtlcCreationInstructions;

export type HtlcSettlementInstructions =
NimiqHtlcSettlementInstructions
| BitcoinHtlcSettlementInstructions
| PolygonHtlcSettlementInstructions
| EuroHtlcSettlementInstructions;
| EuroHtlcSettlementInstructions
| SinpeMovilHtlcSettlementInstructions;

export type HtlcRefundInstructions =
NimiqHtlcRefundInstructions
Expand Down Expand Up @@ -428,6 +451,7 @@ export interface SetupSwapResult {
btc?: SignedBtcTransaction;
usdc?: SignedPolygonTransaction;
eur?: string; // When funding EUR: empty string, when redeeming EUR: JWS of the settlement instructions
crc?: string; // When funding CRC: empty string, when redeeming CRC: JWS of the settlement instructions
refundTx?: string;
}

Expand Down
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"types": "types/index.d.ts",
"dependencies": {
"@nimiq/core-web": "^1.6.1",
"@nimiq/fastspot-api": "^1.8.0",
"@nimiq/fastspot-api": "https://github.com/nimiq/fastspot-api#3a7c4b68529d7ec9ba8955a399412eaae946c528",
"@nimiq/rpc": "^0.4.0",
"@nimiq/utils": "^0.5.0",
"@opengsn/common": "^2.2.5",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"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": "https://github.com/nimiq/fastspot-api#3a7c4b68529d7ec9ba8955a399412eaae946c528",
"@nimiq/iqons": "^1.5.2",
"@nimiq/keyguard-client": "^1.6.0",
"@nimiq/ledger-api": "^2.3.0",
Expand Down
14 changes: 10 additions & 4 deletions src/lib/RequestParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -527,11 +527,11 @@ 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', 'EUR', 'CRC'].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', 'EUR', 'CRC'].includes(setupSwapRequest.redeem.type)) {
throw new Error('Redeeming type is not supported');
}

Expand Down Expand Up @@ -631,7 +631,10 @@ export class RequestParser {
} : setupSwapRequest.fund.type === 'USDC_MATIC' ? {
...setupSwapRequest.fund,
type: SwapAsset[setupSwapRequest.fund.type],
} : { // EUR
} : setupSwapRequest.fund.type === 'EUR' ? {
...setupSwapRequest.fund,
type: SwapAsset[setupSwapRequest.fund.type],
} : { // CRC
...setupSwapRequest.fund,
type: SwapAsset[setupSwapRequest.fund.type],
},
Expand All @@ -649,7 +652,10 @@ export class RequestParser {
} : setupSwapRequest.redeem.type === 'USDC_MATIC' ? {
...setupSwapRequest.redeem,
type: SwapAsset[setupSwapRequest.redeem.type],
} : { // EUR
} : setupSwapRequest.redeem.type === 'EUR' ? {
...setupSwapRequest.redeem,
type: SwapAsset[setupSwapRequest.redeem.type],
} : { // CRC
...setupSwapRequest.redeem,
type: SwapAsset[setupSwapRequest.redeem.type],
},
Expand Down
16 changes: 16 additions & 0 deletions src/lib/RequestTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ export interface ParsedSetupSwapRequest extends ParsedSimpleRequest {
value: number, // Eurocents
fee: number, // Eurocents
bankLabel?: string,
} | {
type: SwapAsset.CRC,
value: number, // CRC cents
fee: number, // CRC cents
recipientLabel?: string,
};

redeem: {
Expand Down Expand Up @@ -251,6 +256,17 @@ export interface ParsedSetupSwapRequest extends ParsedSimpleRequest {
} | {
type: 'mock',
};
} | {
type: SwapAsset.CRC,
value: number; // CRC cents
fee: number; // CRC cents
recipientLabel?: string;
settlement: {
type: 'sinpemovil',
phoneNumber: string,
// } | {
// type: 'mock',
};
};

// Data needed for display
Expand Down
9 changes: 9 additions & 0 deletions src/views/SetupSwap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,15 @@ export default class SetupSwap extends BitcoinSyncBaseView {
};
}
if (this.request.fund.type === SwapAsset.CRC) {
fundingInfo = {
type: SwapAsset.CRC,
amount: this.request.fund.value,
fee: this.request.fund.fee,
recipientLabel: this.request.fund.recipientLabel,
};
}
if (this.request.redeem.type === SwapAsset.NIM) {
const signer = this._account.findSignerForAddress(this.request.redeem.recipient);
if (!signer) {
Expand Down
49 changes: 46 additions & 3 deletions src/views/SetupSwapSuccess.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
case SwapAsset.USDC_MATIC:
redeemAddress = this.request.redeem.request.from;
break;
case SwapAsset.CRC:
case SwapAsset.EUR:
// Assemble recipient object
redeemAddress = {
Expand All @@ -127,7 +128,7 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
confirmedSwap = await confirmSwap({
id: this.request.swapId,
} as PreSwap, this.request.redeem.type === SwapAsset.EUR ? {
} as PreSwap, this.request.redeem.type === SwapAsset.EUR || this.request.redeem.type === SwapAsset.CRC ? {
asset: this.request.redeem.type,
...(redeemAddress as { kty: string, crv: string, x: string }),
} : {
Expand Down Expand Up @@ -276,6 +277,18 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
// TODO: Validate correct recipient public key
}
if (confirmedSwap.from.asset === SwapAsset.CRC || confirmedSwap.to.asset === SwapAsset.CRC) {
// TODO: Fetch contract from OASIS API and compare instead of trusting Fastspot
if (hashRoot && confirmedSwap.hash !== hashRoot) {
this.$rpc.reject(new Error('HTLC hash roots do not match'));
return;
}
hashRoot = confirmedSwap.hash;
// TODO: Validate correct recipient public key
}
if (!hashRoot) {
this.$rpc.reject(new Error('UNEXPECTED: Could not extract swap hash from contracts'));
return;
Expand Down Expand Up @@ -330,6 +343,18 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
};
}
if (this.request.fund.type === SwapAsset.CRC) {
const crcContract = confirmedSwap.contracts[SwapAsset.CRC] as Contract<SwapAsset.CRC>;
const crcHtlcData = crcContract.htlc;
fundingHtlcInfo = {
type: SwapAsset.CRC,
hash: hashRoot,
timeout: crcContract.timeout,
htlcId: crcHtlcData.address,
};
}
if (this.request.redeem.type === SwapAsset.NIM) {
const nimHtlcData = confirmedSwap.contracts[SwapAsset.NIM]!.htlc as NimHtlcDetails;
Expand Down Expand Up @@ -433,6 +458,18 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
};
}
if (this.request.redeem.type === SwapAsset.CRC) {
const crcContract = confirmedSwap.contracts[SwapAsset.CRC] as Contract<SwapAsset.CRC>;
const crcHtlcData = crcContract.htlc;
redeemingHtlcInfo = {
type: SwapAsset.CRC,
hash: hashRoot,
timeout: crcContract.timeout,
htlcId: crcHtlcData.address,
};
}
if (this._isDestroyed) return;
if (!fundingHtlcInfo || !redeemingHtlcInfo) {
Expand All @@ -448,6 +485,7 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
let polygonTransaction: SignedPolygonTransaction | undefined;
let refundTransaction: string | undefined;
let euroSettlement: string | undefined;
let crcSettlement: string | undefined;
try {
const signingResult = await this._signSwapTransactions({
fund: fundingHtlcInfo,
Expand All @@ -459,6 +497,7 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
nimProxy: nimiqProxyTransaction,
btc: bitcoinTransaction,
eur: euroSettlement,
crc: crcSettlement,
usdc: polygonTransaction,
refundTx: refundTransaction,
} = signingResult);
Expand Down Expand Up @@ -505,6 +544,7 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
btc: bitcoinTransaction,
usdc: polygonTransaction,
eur: euroSettlement,
crc: crcSettlement,
refundTx: refundTransaction,
};
Expand All @@ -522,10 +562,10 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
protected _getOasisRecipientPublicKey() {
// note that this method gets overwritten for SetupSwapLedger
if (!this.keyguardResult || !this.keyguardResult.eurPubKey) {
if (!this.keyguardResult || !this.keyguardResult.fiatPubKey) {
throw new Error('Cannot find OASIS recipient public key');
}
return Nimiq.BufferUtils.toBase64Url(Nimiq.BufferUtils.fromHex(this.keyguardResult.eurPubKey))
return Nimiq.BufferUtils.toBase64Url(Nimiq.BufferUtils.fromHex(this.keyguardResult.fiatPubKey))
.replace(/\.*$/, ''); // OASIS cannot handle trailing filler dots
}
Expand All @@ -535,6 +575,7 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
btc?: SignedBtcTransaction,
usdc?: SignedPolygonTransaction,
eur?: string,
crc?: string,
refundTx?: string,
} | null> {
// Note that this method gets overwritten for SetupSwapLedger
Expand All @@ -550,6 +591,7 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
btc: bitcoinTransaction,
usdc: polygonTransaction,
eur: euroSettlement,
crc: crcSettlement,
refundTx,
} = await client.signSwapTransactions(keyguardRequest);
Expand Down Expand Up @@ -584,6 +626,7 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
} : undefined,
usdc: polygonTransaction,
eur: euroSettlement,
crc: crcSettlement,
refundTx,
};
}
Expand Down
7 changes: 3 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1520,10 +1520,9 @@
dependencies:
bitcoinjs-lib "^5.1.10"

"@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/fastspot-api@https://github.com/nimiq/fastspot-api#3a7c4b68529d7ec9ba8955a399412eaae946c528":
version "1.9.0"
resolved "https://github.com/nimiq/fastspot-api#3a7c4b68529d7ec9ba8955a399412eaae946c528"

"@nimiq/iqons@^1.5.2", "@nimiq/iqons@^1.6.0":
version "1.6.0"
Expand Down

0 comments on commit db3455e

Please sign in to comment.