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

Added CRC Adapter #1

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ await swapHandler.settleIncoming(serializedSettlementTx, secret)
/**
* 5. Await confirmation of settled HTLC
*
* This is especially relevant for EUR contracts, as they may take a
* This is especially relevant for EUR and CRC contracts, as they may take a
* few minutes to confirm after they were settled.
*
* The optional `onUpdate` callback receives the transaction object:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"dependencies": {
"@nimiq/core-web": "^1.5.8",
"@nimiq/electrum-client": "https://github.com/nimiq/electrum-client#build",
"@nimiq/oasis-api": "^1.0.1",
"@nimiq/oasis-api": "github:nimiq/oasis-api-js#fbd74179a5b9590f8e6dfe9b08ca47122db2ce1e",
"ethers": "^5.7.2",
"promise.prototype.finally": "^3.1.3"
},
Expand Down
42 changes: 21 additions & 21 deletions src/EuroAssetAdapter.ts → src/FiatAssetAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { Htlc as OasisHtlc, HtlcStatus, SettlementInstruction, SettlementStatus } from '@nimiq/oasis-api';
import { AssetAdapter, SwapAsset } from './IAssetAdapter';
import { Htlc, HtlcStatus, SettlementInstruction, SettlementStatus, SettlementTokens } from '@nimiq/oasis-api';
import type { AssetAdapter, FiatSwapAsset } from './IAssetAdapter';

export type HtlcDetails = OasisHtlc;
export { Htlc as OasisHtlcDetails, SettlementTokens as OasisSettlementTokens };

export interface OasisClient {
getHtlc(id: string): Promise<HtlcDetails>;
settleHtlc(id: string, secret: string, settlementJWS: string, authorizationToken?: string): Promise<HtlcDetails>;
getHtlc(id: string): Promise<Htlc>;
settleHtlc(id: string, secret: string, settlementJWS: string, tokens?: SettlementTokens): Promise<Htlc>;
}

export class EuroAssetAdapter implements AssetAdapter<SwapAsset.EUR> {
export class FiatAssetAdapter implements AssetAdapter<FiatSwapAsset> {
private cancelCallback: ((reason: Error) => void) | null = null;
private stopped = false;

constructor(public client: OasisClient) {}

private async findTransaction(
id: string,
test: (htlc: HtlcDetails) => boolean,
): Promise<HtlcDetails> {
const check = async (): Promise<HtlcDetails | null> => {
test: (htlc: Htlc) => boolean,
): Promise<Htlc> {
const check = async (): Promise<Htlc | null> => {
try {
const htlc = await this.client.getHtlc(id);
if (test(htlc)) return htlc;
Expand Down Expand Up @@ -64,8 +64,8 @@ export class EuroAssetAdapter implements AssetAdapter<SwapAsset.EUR> {
value: number,
data?: string,
confirmations?: number,
onUpdate?: (htlc: HtlcDetails) => any,
): Promise<HtlcDetails> {
onUpdate?: (htlc: Htlc) => any,
): Promise<Htlc> {
return this.findTransaction(
id,
(htlc) => {
Expand All @@ -81,15 +81,15 @@ export class EuroAssetAdapter implements AssetAdapter<SwapAsset.EUR> {
}

// eslint-disable-next-line class-methods-use-this
public async fundHtlc(): Promise<HtlcDetails> {
throw new Error('Method "fundHtlc" not available for EUR HTLCs');
public async fundHtlc(): Promise<Htlc> {
throw new Error('Method "fundHtlc" not available for EUR/CRC HTLCs');
}

public async awaitHtlcSettlement(id: string): Promise<OasisHtlc<HtlcStatus.SETTLED>> {
public async awaitHtlcSettlement(id: string): Promise<Htlc<HtlcStatus.SETTLED>> {
return this.findTransaction(
id,
(htlc) => typeof htlc.preimage.value === 'string',
) as Promise<OasisHtlc<HtlcStatus.SETTLED>>;
) as Promise<Htlc<HtlcStatus.SETTLED>>;
}

public async awaitSwapSecret(id: string): Promise<string> {
Expand All @@ -101,18 +101,18 @@ export class EuroAssetAdapter implements AssetAdapter<SwapAsset.EUR> {
settlementJWS: string,
secret: string,
hash: string,
authorizationToken?: string,
): Promise<HtlcDetails> {
if (this.stopped) throw new Error('EuroAssetAdapter called while stopped');
tokens?: SettlementTokens,
): Promise<Htlc> {
if (this.stopped) throw new Error('FiatAssetAdapter called while stopped');

const jwsBody = settlementJWS.split('.')[1];
// JWS is encoded as Base64Url
const jsonBody = atob(jwsBody.replace(/_/g, '/').replace(/-/g, '+'));
const payload = JSON.parse(jsonBody) as SettlementInstruction;

let htlc: HtlcDetails;
let htlc: Htlc;
try {
htlc = await this.client.settleHtlc(payload.contractId, secret, settlementJWS, authorizationToken);
htlc = await this.client.settleHtlc(payload.contractId, secret, settlementJWS, tokens);
} catch (error) {
console.error(error); // eslint-disable-line no-console
htlc = await this.client.getHtlc(payload.contractId);
Expand All @@ -125,7 +125,7 @@ export class EuroAssetAdapter implements AssetAdapter<SwapAsset.EUR> {
return htlc;
}

public async awaitSettlementConfirmation(id: string, onUpdate?: (tx: HtlcDetails) => any): Promise<HtlcDetails> {
public async awaitSettlementConfirmation(id: string, onUpdate?: (tx: Htlc) => any): Promise<Htlc> {
return this.findTransaction(id, (htlc) => {
if (htlc.status !== HtlcStatus.SETTLED) return false;

Expand Down
17 changes: 10 additions & 7 deletions src/IAssetAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BitcoinClient, TransactionDetails as BitcoinTransactionDetails } from './BitcoinAssetAdapter';
import { GenericEvent as Erc20TransactionDetails, Web3Client } from './Erc20AssetAdapter';
import { HtlcDetails as EuroHtlcDetails, OasisClient } from './EuroAssetAdapter';
import { NimiqClient, TransactionDetails as NimiqTransactionDetails } from './NimiqAssetAdapter';
import type { BitcoinClient, TransactionDetails as BitcoinTransactionDetails } from './BitcoinAssetAdapter';
import type { GenericEvent as Erc20TransactionDetails, Web3Client } from './Erc20AssetAdapter';
import type { OasisClient, OasisHtlcDetails, OasisSettlementTokens } from './FiatAssetAdapter';
import type { NimiqClient, TransactionDetails as NimiqTransactionDetails } from './NimiqAssetAdapter';

export enum SwapAsset {
NIM = 'NIM',
Expand All @@ -10,18 +10,21 @@ export enum SwapAsset {
USDC_MATIC = 'USDC_MATIC',
USDT = 'USDT', // Alternatively USDT_MATIC
EUR = 'EUR',
CRC = 'CRC',
}

export type FiatSwapAsset = SwapAsset.EUR | SwapAsset.CRC;

export type Transaction<TAsset extends SwapAsset> = TAsset extends SwapAsset.NIM ? NimiqTransactionDetails
: TAsset extends SwapAsset.BTC ? BitcoinTransactionDetails
: TAsset extends SwapAsset.USDC | SwapAsset.USDC_MATIC | SwapAsset.USDT ? Erc20TransactionDetails
: TAsset extends SwapAsset.EUR ? EuroHtlcDetails
: TAsset extends FiatSwapAsset ? OasisHtlcDetails
: never;

export type Client<TAsset extends SwapAsset> = TAsset extends SwapAsset.NIM ? NimiqClient
: TAsset extends SwapAsset.BTC ? BitcoinClient
: TAsset extends SwapAsset.USDC | SwapAsset.USDC_MATIC | SwapAsset.USDT ? Web3Client
: TAsset extends SwapAsset.EUR ? OasisClient
: TAsset extends FiatSwapAsset ? OasisClient
: never;

export interface AssetAdapter<TAsset extends SwapAsset> {
Expand Down Expand Up @@ -49,7 +52,7 @@ export interface AssetAdapter<TAsset extends SwapAsset> {
serializedTx: string,
secret: string,
hash: string,
authorizationToken?: string,
tokens?: OasisSettlementTokens,
): Promise<Transaction<TAsset>>;

awaitSettlementConfirmation(
Expand Down
15 changes: 9 additions & 6 deletions src/SwapHandler.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { BitcoinAssetAdapter } from './BitcoinAssetAdapter';
import { Erc20AssetAdapter } from './Erc20AssetAdapter';
import { EuroAssetAdapter } from './EuroAssetAdapter';
import { AssetAdapter, SwapAsset } from './IAssetAdapter';
import type { Client, Transaction } from './IAssetAdapter';
import { FiatAssetAdapter } from './FiatAssetAdapter';
import type { OasisSettlementTokens } from './FiatAssetAdapter';
import { SwapAsset } from './IAssetAdapter';
import type { AssetAdapter, Client, Transaction } from './IAssetAdapter';
import { NimiqAssetAdapter } from './NimiqAssetAdapter';

// Re-export to centralize exports
Expand Down Expand Up @@ -54,7 +55,9 @@ export class SwapHandler<FromAsset extends SwapAsset, ToAsset extends SwapAsset>
SwapAsset
>;
case SwapAsset.EUR:
return new EuroAssetAdapter(client as Client<SwapAsset.EUR>) as AssetAdapter<SwapAsset>;
return new FiatAssetAdapter(client as Client<SwapAsset.EUR>) as AssetAdapter<SwapAsset>;
case SwapAsset.CRC:
return new FiatAssetAdapter(client as Client<SwapAsset.CRC>) as AssetAdapter<SwapAsset>;
default:
throw new Error(`Unsupported asset: ${asset}`);
}
Expand Down Expand Up @@ -121,13 +124,13 @@ export class SwapHandler<FromAsset extends SwapAsset, ToAsset extends SwapAsset>
public async settleIncoming(
serializedTx: string,
secret: string,
authorizationToken?: string,
tokens?: OasisSettlementTokens,
): Promise<Transaction<ToAsset>> {
return this.toAssetAdapter.settleHtlc(
serializedTx,
secret,
this.swap.hash,
authorizationToken,
tokens,
);
}

Expand Down
5 changes: 2 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -395,10 +395,9 @@
dependencies:
bitcoinjs-lib "^5.1.10"

"@nimiq/oasis-api@^1.0.1":
"@nimiq/oasis-api@github:nimiq/oasis-api-js#fbd74179a5b9590f8e6dfe9b08ca47122db2ce1e":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@nimiq/oasis-api/-/oasis-api-1.1.1.tgz#d1aa3d0f9afabf9116205809a0fc72d2e392bafa"
integrity sha512-jXNO7nvE0mzAyDCrucByv5AcyDWZjyqb9810YctysxqUtOxNG27hPkhomL+WW6nrnTVgvriJo3yaTFPTfLphKw==
resolved "https://codeload.github.com/nimiq/oasis-api-js/tar.gz/fbd74179a5b9590f8e6dfe9b08ca47122db2ce1e"

"@oxlint/[email protected]":
version "0.10.3"
Expand Down