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 authored and sisou committed Aug 13, 2024
1 parent f418320 commit 492ecdc
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 19 deletions.
27 changes: 25 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,19 @@ export interface EuroHtlcSettlementInstructions {
};
}

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

export interface NimiqHtlcRefundInstructions {
type: 'NIM';
sender: string; // HTLC address
Expand Down Expand Up @@ -368,13 +388,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 +450,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
7 changes: 3 additions & 4 deletions client/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,9 @@
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/fastspot-api@https://github.com/nimiq/fastspot-api#3a7c4b68529d7ec9ba8955a399412eaae946c528":
version "1.9.0"
resolved "https://github.com/nimiq/fastspot-api#3a7c4b68529d7ec9ba8955a399412eaae946c528"

"@nimiq/rpc@^0.4.0":
version "0.4.0"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,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
senderLabel?: 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
20 changes: 20 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,
senderLabel: this.request.fund.senderLabel,
};
}
if (this.request.redeem.type === SwapAsset.NIM) {
const signer = this._account.findSignerForAddress(this.request.redeem.recipient);
if (!signer) {
Expand Down Expand Up @@ -282,6 +291,17 @@ export default class SetupSwap extends BitcoinSyncBaseView {
};
}
if (this.request.redeem.type === SwapAsset.CRC) {
redeemingInfo = {
type: SwapAsset.CRC,
keyPath: DEFAULT_KEY_PATH,
settlement: this.request.redeem.settlement,
amount: this.request.redeem.value,
fee: this.request.redeem.fee,
phoneNumber: this.request.redeem.phoneNumber,
};
}
if (!fundingInfo || !redeemingInfo) {
throw new Error('Funding or redeeming info missing.');
}
Expand Down
55 changes: 52 additions & 3 deletions src/views/SetupSwapSuccess.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
// Confirm swap to Fastspot and get contract details
this.state = this.State.FETCHING_SWAP_DATA;
console.log('Confirming swap', this.request.swapId, Config.fastspot.apiEndpoint);
initFastspotApi(Config.fastspot.apiEndpoint, Config.fastspot.apiKey);
console.log('Fastspot API initialized');
let refundAddress = '';
switch (this.request.fund.type) {
Expand All @@ -101,6 +103,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 @@ -118,16 +121,19 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
let confirmedSwap: Swap;
try {
const uid = this.request.kyc ? this.request.kyc.userId : await walletInfo.getUid();
console.log('UID:', uid);
const s3GrantToken = this.request.kyc ? this.request.kyc.s3GrantToken : undefined;
console.log('S3 grant token:', s3GrantToken);
let oasisClearingAuthorizationToken: string | undefined;
if (this.request.kyc && this.request.kyc.oasisGrantToken && this.request.fund.type === SwapAsset.EUR) {
initOasisApi(Config.oasis.apiEndpoint);
oasisClearingAuthorizationToken = await exchangeAuthorizationToken(this.request.kyc.oasisGrantToken);
}
console.log('Confirming swap', this.request.swapId, uid, refundAddress, redeemAddress, s3GrantToken, oasisClearingAuthorizationToken);
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 All @@ -144,6 +150,7 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
} else if (error.message.includes('503')) {
throw new Error(this.$t('503 Service unavailable - please try again later') as string);
} else {
console.error('oh no');
throw error;
}
});
Expand Down Expand Up @@ -276,6 +283,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 +349,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 +464,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 +491,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 +503,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 +550,7 @@ export default class SetupSwapSuccess extends BitcoinSyncBaseView {
btc: bitcoinTransaction,
usdc: polygonTransaction,
eur: euroSettlement,
crc: crcSettlement,
refundTx: refundTransaction,
};
Expand All @@ -522,10 +568,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 +581,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 +597,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 +632,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 @@ -2012,10 +2012,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 492ecdc

Please sign in to comment.