@arkade-os/skill 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +15 -0
- package/cli/arkade.mjs +121 -53
- package/dist/cjs/skills/index.js.map +1 -1
- package/dist/cjs/skills/lendaswap.js +244 -312
- package/dist/cjs/skills/lendaswap.js.map +1 -1
- package/dist/esm/skills/index.js.map +1 -1
- package/dist/esm/skills/lendaswap.js +244 -312
- package/dist/esm/skills/lendaswap.js.map +1 -1
- package/dist/types/skills/index.d.ts +1 -1
- package/dist/types/skills/index.d.ts.map +1 -1
- package/dist/types/skills/lendaswap.d.ts +40 -74
- package/dist/types/skills/lendaswap.d.ts.map +1 -1
- package/dist/types/skills/types.d.ts +80 -12
- package/dist/types/skills/types.d.ts.map +1 -1
- package/package.json +3 -2
|
@@ -2,18 +2,15 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* LendaSwapSkill - Swap USDC/USDT from/to Arkade via LendaSwap.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Uses the @lendasat/lendaswap-sdk-pure SDK for non-custodial
|
|
6
|
+
* BTC/stablecoin atomic swaps via HTLCs.
|
|
7
7
|
*
|
|
8
8
|
* @module skills/lendaswap
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.LendaSwapSkill = void 0;
|
|
12
12
|
exports.createLendaSwapSkill = createLendaSwapSkill;
|
|
13
|
-
|
|
14
|
-
* Default LendaSwap API URL.
|
|
15
|
-
*/
|
|
16
|
-
const DEFAULT_API_URL = "https://apilendaswap.lendasat.com/";
|
|
13
|
+
const lendaswap_sdk_pure_1 = require("@lendasat/lendaswap-sdk-pure");
|
|
17
14
|
/**
|
|
18
15
|
* Token decimals for stablecoins.
|
|
19
16
|
*/
|
|
@@ -25,389 +22,325 @@ const TOKEN_DECIMALS = {
|
|
|
25
22
|
usdt_eth: 6,
|
|
26
23
|
usdt_arb: 6,
|
|
27
24
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Map SDK swap status to our simplified status.
|
|
27
|
+
*/
|
|
28
|
+
function mapSwapStatus(sdkStatus) {
|
|
29
|
+
switch (sdkStatus) {
|
|
30
|
+
case "pending":
|
|
31
|
+
return "pending";
|
|
32
|
+
case "clientfundingseen":
|
|
33
|
+
case "clientfunded":
|
|
34
|
+
return "funded";
|
|
35
|
+
case "serverfunded":
|
|
36
|
+
case "clientredeeming":
|
|
37
|
+
return "processing";
|
|
38
|
+
case "clientredeemed":
|
|
39
|
+
case "serverredeemed":
|
|
40
|
+
case "clientredeemedandclientrefunded":
|
|
41
|
+
return "completed";
|
|
42
|
+
case "expired":
|
|
43
|
+
case "clientfundedtoolate":
|
|
44
|
+
return "expired";
|
|
45
|
+
case "clientrefunded":
|
|
46
|
+
case "clientfundedserverrefunded":
|
|
47
|
+
case "clientrefundedserverfunded":
|
|
48
|
+
case "clientrefundedserverrefunded":
|
|
49
|
+
return "refunded";
|
|
50
|
+
case "clientinvalidfunded":
|
|
51
|
+
return "failed";
|
|
52
|
+
default:
|
|
53
|
+
return "pending";
|
|
41
54
|
}
|
|
42
|
-
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check if a status is terminal (no longer active).
|
|
58
|
+
*/
|
|
59
|
+
function isTerminalStatus(status) {
|
|
60
|
+
return (status === "completed" ||
|
|
61
|
+
status === "expired" ||
|
|
62
|
+
status === "refunded" ||
|
|
63
|
+
status === "failed");
|
|
43
64
|
}
|
|
44
65
|
/**
|
|
45
66
|
* LendaSwapSkill provides stablecoin swap capabilities for Arkade wallets
|
|
46
|
-
* using the LendaSwap SDK.
|
|
47
|
-
*
|
|
48
|
-
* This skill enables:
|
|
49
|
-
* - Swapping BTC from Arkade to USDC/USDT on Polygon, Ethereum, or Arbitrum
|
|
50
|
-
* - Receiving USDC/USDT and converting to BTC in your Arkade wallet
|
|
67
|
+
* using the LendaSwap SDK for non-custodial atomic swaps.
|
|
51
68
|
*
|
|
52
69
|
* @example
|
|
53
70
|
* ```typescript
|
|
54
|
-
* import { Wallet, SingleKey } from "@arkade-os/sdk";
|
|
55
|
-
* import { LendaSwapSkill } from "@arkade-os/skill";
|
|
56
|
-
*
|
|
57
|
-
* // Create a wallet
|
|
58
|
-
* const wallet = await Wallet.create({
|
|
59
|
-
* identity: SingleKey.fromHex(privateKeyHex),
|
|
60
|
-
* arkServerUrl: "https://arkade.computer",
|
|
61
|
-
* });
|
|
62
|
-
*
|
|
63
|
-
* // Create the LendaSwap skill
|
|
64
71
|
* const lendaswap = new LendaSwapSkill({ wallet });
|
|
65
72
|
*
|
|
66
|
-
* // Get a quote
|
|
73
|
+
* // Get a quote
|
|
67
74
|
* const quote = await lendaswap.getQuoteBtcToStablecoin(100000, "usdc_pol");
|
|
68
|
-
* console.log("You'll receive:", quote.targetAmount, "USDC");
|
|
69
75
|
*
|
|
70
|
-
* //
|
|
76
|
+
* // Create a swap (BTC → USDC)
|
|
71
77
|
* const result = await lendaswap.swapBtcToStablecoin({
|
|
72
78
|
* targetAddress: "0x...",
|
|
73
79
|
* targetToken: "usdc_pol",
|
|
74
80
|
* targetChain: "polygon",
|
|
75
81
|
* sourceAmount: 100000,
|
|
76
82
|
* });
|
|
77
|
-
*
|
|
83
|
+
*
|
|
84
|
+
* // Fund the VHTLC address from your Arkade wallet, then claim
|
|
85
|
+
* const claim = await lendaswap.claimSwap(result.swapId);
|
|
78
86
|
* ```
|
|
79
87
|
*/
|
|
80
88
|
class LendaSwapSkill {
|
|
81
|
-
/**
|
|
82
|
-
* Creates a new LendaSwapSkill instance.
|
|
83
|
-
*
|
|
84
|
-
* @param config - Configuration options
|
|
85
|
-
*/
|
|
86
89
|
constructor(config) {
|
|
87
90
|
this.name = "lendaswap";
|
|
88
91
|
this.description = "Swap USDC/USDT from/to Arkade via LendaSwap non-custodial exchange";
|
|
89
|
-
this.version = "
|
|
90
|
-
this.
|
|
92
|
+
this.version = "2.0.0";
|
|
93
|
+
this.client = null;
|
|
91
94
|
this.wallet = config.wallet;
|
|
92
|
-
this.apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
93
|
-
this.apiKey = config.apiKey;
|
|
94
95
|
this.referralCode = config.referralCode;
|
|
96
|
+
this.config = config;
|
|
95
97
|
}
|
|
96
98
|
/**
|
|
97
|
-
*
|
|
99
|
+
* Get or create the LendaSwap SDK client (lazy initialization).
|
|
98
100
|
*/
|
|
101
|
+
async getClient() {
|
|
102
|
+
if (this.client)
|
|
103
|
+
return this.client;
|
|
104
|
+
const builder = lendaswap_sdk_pure_1.Client.builder()
|
|
105
|
+
.withSignerStorage(this.config.walletStorage || new lendaswap_sdk_pure_1.InMemoryWalletStorage())
|
|
106
|
+
.withSwapStorage(this.config.swapStorage || new lendaswap_sdk_pure_1.InMemorySwapStorage());
|
|
107
|
+
if (this.config.apiUrl) {
|
|
108
|
+
builder.withBaseUrl(this.config.apiUrl);
|
|
109
|
+
}
|
|
110
|
+
if (this.config.apiKey) {
|
|
111
|
+
builder.withApiKey(this.config.apiKey);
|
|
112
|
+
}
|
|
113
|
+
if (this.config.esploraUrl) {
|
|
114
|
+
builder.withEsploraUrl(this.config.esploraUrl);
|
|
115
|
+
}
|
|
116
|
+
if (this.config.arkadeServerUrl) {
|
|
117
|
+
builder.withArkadeServerUrl(this.config.arkadeServerUrl);
|
|
118
|
+
}
|
|
119
|
+
if (this.config.mnemonic) {
|
|
120
|
+
builder.withMnemonic(this.config.mnemonic);
|
|
121
|
+
}
|
|
122
|
+
this.client = await builder.build();
|
|
123
|
+
return this.client;
|
|
124
|
+
}
|
|
99
125
|
async isAvailable() {
|
|
100
126
|
try {
|
|
101
|
-
const
|
|
102
|
-
|
|
127
|
+
const client = await this.getClient();
|
|
128
|
+
const result = await client.healthCheck();
|
|
129
|
+
return result === "ok";
|
|
103
130
|
}
|
|
104
131
|
catch {
|
|
105
132
|
return false;
|
|
106
133
|
}
|
|
107
134
|
}
|
|
108
135
|
/**
|
|
109
|
-
* Get
|
|
136
|
+
* Get the LendaSwap mnemonic for persistence across sessions.
|
|
137
|
+
*/
|
|
138
|
+
async getMnemonic() {
|
|
139
|
+
const client = await this.getClient();
|
|
140
|
+
return client.getMnemonic();
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get the API version info.
|
|
110
144
|
*/
|
|
145
|
+
async getVersion() {
|
|
146
|
+
const client = await this.getClient();
|
|
147
|
+
return client.getVersion();
|
|
148
|
+
}
|
|
111
149
|
async getQuoteBtcToStablecoin(sourceAmount, targetToken) {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
amount: sourceAmount,
|
|
118
|
-
}),
|
|
119
|
-
});
|
|
120
|
-
if (!response.ok) {
|
|
121
|
-
throw new Error(`Failed to get quote: ${response.statusText}`);
|
|
122
|
-
}
|
|
123
|
-
const data = (await response.json());
|
|
150
|
+
const client = await this.getClient();
|
|
151
|
+
const quote = await client.getQuote("btc_arkade", targetToken, sourceAmount);
|
|
152
|
+
const rate = parseFloat(quote.exchange_rate);
|
|
153
|
+
const netSats = sourceAmount - quote.protocol_fee - quote.network_fee;
|
|
154
|
+
const targetAmount = (netSats / 1e8) * rate;
|
|
124
155
|
return {
|
|
125
156
|
sourceToken: "btc_arkade",
|
|
126
157
|
targetToken,
|
|
127
158
|
sourceAmount,
|
|
128
|
-
targetAmount
|
|
129
|
-
exchangeRate:
|
|
159
|
+
targetAmount,
|
|
160
|
+
exchangeRate: rate,
|
|
130
161
|
fee: {
|
|
131
|
-
amount:
|
|
132
|
-
percentage:
|
|
162
|
+
amount: quote.protocol_fee + quote.network_fee,
|
|
163
|
+
percentage: quote.protocol_fee_rate * 100,
|
|
133
164
|
},
|
|
134
|
-
expiresAt:
|
|
165
|
+
expiresAt: new Date(Date.now() + 60000),
|
|
135
166
|
};
|
|
136
167
|
}
|
|
137
|
-
/**
|
|
138
|
-
* Get a quote for swapping stablecoins to BTC.
|
|
139
|
-
*/
|
|
140
168
|
async getQuoteStablecoinToBtc(sourceAmount, sourceToken) {
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
amount: sourceAmount,
|
|
147
|
-
}),
|
|
148
|
-
});
|
|
149
|
-
if (!response.ok) {
|
|
150
|
-
throw new Error(`Failed to get quote: ${response.statusText}`);
|
|
151
|
-
}
|
|
152
|
-
const data = (await response.json());
|
|
169
|
+
const client = await this.getClient();
|
|
170
|
+
const quote = await client.getQuote(sourceToken, "btc_arkade", sourceAmount);
|
|
171
|
+
const rate = parseFloat(quote.exchange_rate);
|
|
172
|
+
const grossSats = (sourceAmount / rate) * 1e8;
|
|
173
|
+
const targetAmount = grossSats - quote.protocol_fee - quote.network_fee;
|
|
153
174
|
return {
|
|
154
175
|
sourceToken,
|
|
155
176
|
targetToken: "btc_arkade",
|
|
156
177
|
sourceAmount,
|
|
157
|
-
targetAmount:
|
|
158
|
-
exchangeRate:
|
|
178
|
+
targetAmount: Math.max(0, Math.floor(targetAmount)),
|
|
179
|
+
exchangeRate: rate,
|
|
159
180
|
fee: {
|
|
160
|
-
amount:
|
|
161
|
-
percentage:
|
|
181
|
+
amount: quote.protocol_fee + quote.network_fee,
|
|
182
|
+
percentage: quote.protocol_fee_rate * 100,
|
|
162
183
|
},
|
|
163
|
-
expiresAt:
|
|
184
|
+
expiresAt: new Date(Date.now() + 60000),
|
|
164
185
|
};
|
|
165
186
|
}
|
|
166
|
-
/**
|
|
167
|
-
* Swap BTC from Arkade to stablecoins on EVM.
|
|
168
|
-
*/
|
|
169
187
|
async swapBtcToStablecoin(params) {
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
body: JSON.stringify({
|
|
174
|
-
sourceAddress: arkAddress,
|
|
175
|
-
targetAddress: params.targetAddress,
|
|
176
|
-
targetToken: params.targetToken,
|
|
177
|
-
targetChain: params.targetChain,
|
|
178
|
-
sourceAmount: params.sourceAmount,
|
|
179
|
-
targetAmount: params.targetAmount,
|
|
180
|
-
referralCode: params.referralCode || this.referralCode,
|
|
181
|
-
}),
|
|
182
|
-
});
|
|
183
|
-
if (!response.ok) {
|
|
184
|
-
throw new Error(`Failed to create swap: ${response.statusText}`);
|
|
185
|
-
}
|
|
186
|
-
const data = (await response.json());
|
|
187
|
-
const resolvedSourceAmount = params.sourceAmount ?? data.sourceAmount ?? 0;
|
|
188
|
-
// Store swap locally
|
|
189
|
-
const storedSwap = {
|
|
190
|
-
swapId: data.swapId,
|
|
191
|
-
direction: "btc_to_stablecoin",
|
|
192
|
-
status: "awaiting_funding",
|
|
193
|
-
sourceToken: "btc_arkade",
|
|
188
|
+
const client = await this.getClient();
|
|
189
|
+
const result = await client.createArkadeToEvmSwap({
|
|
190
|
+
targetAddress: params.targetAddress,
|
|
194
191
|
targetToken: params.targetToken,
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
this.swapStorage.set(data.swapId, storedSwap);
|
|
192
|
+
targetChain: params.targetChain,
|
|
193
|
+
sourceAmount: params.sourceAmount,
|
|
194
|
+
targetAmount: params.targetAmount,
|
|
195
|
+
referralCode: params.referralCode || this.referralCode,
|
|
196
|
+
});
|
|
197
|
+
const resp = result.response;
|
|
202
198
|
return {
|
|
203
|
-
swapId:
|
|
204
|
-
status:
|
|
205
|
-
sourceAmount:
|
|
206
|
-
targetAmount:
|
|
207
|
-
exchangeRate:
|
|
199
|
+
swapId: resp.id,
|
|
200
|
+
status: mapSwapStatus(resp.status),
|
|
201
|
+
sourceAmount: resp.source_amount,
|
|
202
|
+
targetAmount: resp.target_amount,
|
|
203
|
+
exchangeRate: 0,
|
|
208
204
|
fee: {
|
|
209
|
-
amount:
|
|
210
|
-
percentage:
|
|
205
|
+
amount: resp.fee_sats,
|
|
206
|
+
percentage: 0,
|
|
211
207
|
},
|
|
212
|
-
expiresAt: new Date(
|
|
208
|
+
expiresAt: new Date(resp.vhtlc_refund_locktime * 1000),
|
|
213
209
|
paymentDetails: {
|
|
214
|
-
address:
|
|
210
|
+
address: resp.htlc_address_arkade,
|
|
215
211
|
},
|
|
216
212
|
};
|
|
217
213
|
}
|
|
218
|
-
/**
|
|
219
|
-
* Swap stablecoins from EVM to BTC on Arkade.
|
|
220
|
-
*/
|
|
221
214
|
async swapStablecoinToBtc(params) {
|
|
222
|
-
const
|
|
223
|
-
const
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
body: JSON.stringify({
|
|
227
|
-
sourceChain: params.sourceChain,
|
|
228
|
-
sourceToken: params.sourceToken,
|
|
229
|
-
sourceAmount: params.sourceAmount,
|
|
230
|
-
targetAddress,
|
|
231
|
-
userAddress: params.userAddress,
|
|
232
|
-
referralCode: params.referralCode || this.referralCode,
|
|
233
|
-
}),
|
|
234
|
-
});
|
|
235
|
-
if (!response.ok) {
|
|
236
|
-
throw new Error(`Failed to create swap: ${response.statusText}`);
|
|
237
|
-
}
|
|
238
|
-
const data = (await response.json());
|
|
239
|
-
// Store swap locally
|
|
240
|
-
const storedSwap = {
|
|
241
|
-
swapId: data.swapId,
|
|
242
|
-
direction: "stablecoin_to_btc",
|
|
243
|
-
status: "awaiting_funding",
|
|
215
|
+
const client = await this.getClient();
|
|
216
|
+
const arkAddress = params.targetAddress || (await this.wallet.getAddress());
|
|
217
|
+
const result = await client.createEvmToArkadeSwap({
|
|
218
|
+
sourceChain: params.sourceChain,
|
|
244
219
|
sourceToken: params.sourceToken,
|
|
245
|
-
targetToken: "btc_arkade",
|
|
246
220
|
sourceAmount: params.sourceAmount,
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
this.swapStorage.set(data.swapId, storedSwap);
|
|
221
|
+
targetAddress: arkAddress,
|
|
222
|
+
userAddress: params.userAddress,
|
|
223
|
+
referralCode: params.referralCode || this.referralCode,
|
|
224
|
+
});
|
|
225
|
+
const resp = result.response;
|
|
253
226
|
return {
|
|
254
|
-
swapId:
|
|
255
|
-
status:
|
|
256
|
-
sourceAmount:
|
|
257
|
-
targetAmount:
|
|
258
|
-
exchangeRate:
|
|
227
|
+
swapId: resp.id,
|
|
228
|
+
status: mapSwapStatus(resp.status),
|
|
229
|
+
sourceAmount: resp.source_amount,
|
|
230
|
+
targetAmount: resp.target_amount,
|
|
231
|
+
exchangeRate: 0,
|
|
259
232
|
fee: {
|
|
260
|
-
amount:
|
|
261
|
-
percentage:
|
|
233
|
+
amount: resp.fee_sats,
|
|
234
|
+
percentage: 0,
|
|
262
235
|
},
|
|
263
|
-
expiresAt: new Date(
|
|
236
|
+
expiresAt: new Date(resp.evm_refund_locktime * 1000),
|
|
264
237
|
paymentDetails: {
|
|
265
|
-
address:
|
|
266
|
-
callData:
|
|
238
|
+
address: resp.htlc_address_evm,
|
|
239
|
+
callData: resp.source_token_address,
|
|
267
240
|
},
|
|
268
241
|
};
|
|
269
242
|
}
|
|
270
|
-
/**
|
|
271
|
-
* Get the status of a swap.
|
|
272
|
-
*/
|
|
273
243
|
async getSwapStatus(swapId) {
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const stored = this.swapStorage.get(swapId);
|
|
281
|
-
if (stored) {
|
|
282
|
-
stored.status = data.status;
|
|
283
|
-
stored.txid = data.txid;
|
|
284
|
-
if (data.status === "completed") {
|
|
285
|
-
stored.completedAt = Date.now();
|
|
286
|
-
}
|
|
287
|
-
}
|
|
244
|
+
const client = await this.getClient();
|
|
245
|
+
const data = await client.getSwap(swapId, { updateStorage: true });
|
|
246
|
+
const direction = data.direction === "evm_to_btc"
|
|
247
|
+
? "stablecoin_to_btc"
|
|
248
|
+
: "btc_to_stablecoin";
|
|
249
|
+
const status = mapSwapStatus(data.status);
|
|
288
250
|
return {
|
|
289
251
|
id: swapId,
|
|
290
|
-
direction
|
|
291
|
-
status
|
|
292
|
-
sourceToken: data.
|
|
293
|
-
targetToken: data.
|
|
294
|
-
sourceAmount: data.
|
|
295
|
-
targetAmount: data.
|
|
296
|
-
exchangeRate:
|
|
297
|
-
createdAt: new Date(data.
|
|
298
|
-
completedAt:
|
|
299
|
-
txid: data
|
|
252
|
+
direction,
|
|
253
|
+
status,
|
|
254
|
+
sourceToken: data.source_token,
|
|
255
|
+
targetToken: data.target_token,
|
|
256
|
+
sourceAmount: data.source_amount,
|
|
257
|
+
targetAmount: data.target_amount,
|
|
258
|
+
exchangeRate: 0,
|
|
259
|
+
createdAt: new Date(data.created_at),
|
|
260
|
+
completedAt: status === "completed" ? new Date() : undefined,
|
|
261
|
+
txid: ("evm_htlc_claim_txid" in data
|
|
262
|
+
? data.evm_htlc_claim_txid
|
|
263
|
+
: undefined) ?? undefined,
|
|
300
264
|
};
|
|
301
265
|
}
|
|
302
|
-
/**
|
|
303
|
-
* Get pending stablecoin swaps.
|
|
304
|
-
*/
|
|
305
266
|
async getPendingSwaps() {
|
|
267
|
+
const client = await this.getClient();
|
|
268
|
+
const allSwaps = await client.listAllSwaps();
|
|
306
269
|
const pending = [];
|
|
307
|
-
for (const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
swap.status !== "refunded" &&
|
|
311
|
-
swap.status !== "failed") {
|
|
270
|
+
for (const stored of allSwaps) {
|
|
271
|
+
const status = mapSwapStatus(stored.response.status);
|
|
272
|
+
if (!isTerminalStatus(status)) {
|
|
312
273
|
try {
|
|
313
|
-
const
|
|
314
|
-
pending.push(
|
|
274
|
+
const info = await this.getSwapStatus(stored.swapId);
|
|
275
|
+
pending.push(info);
|
|
315
276
|
}
|
|
316
277
|
catch {
|
|
317
|
-
|
|
318
|
-
pending.push({
|
|
319
|
-
id,
|
|
320
|
-
direction: swap.direction,
|
|
321
|
-
status: swap.status,
|
|
322
|
-
sourceToken: swap.sourceToken,
|
|
323
|
-
targetToken: swap.targetToken,
|
|
324
|
-
sourceAmount: swap.sourceAmount,
|
|
325
|
-
targetAmount: swap.targetAmount,
|
|
326
|
-
exchangeRate: swap.exchangeRate,
|
|
327
|
-
createdAt: new Date(swap.createdAt),
|
|
328
|
-
});
|
|
278
|
+
pending.push(this.storedSwapToInfo(stored));
|
|
329
279
|
}
|
|
330
280
|
}
|
|
331
281
|
}
|
|
332
282
|
return pending;
|
|
333
283
|
}
|
|
334
|
-
/**
|
|
335
|
-
* Get stablecoin swap history.
|
|
336
|
-
*/
|
|
337
284
|
async getSwapHistory() {
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
status: swap.status,
|
|
344
|
-
sourceToken: swap.sourceToken,
|
|
345
|
-
targetToken: swap.targetToken,
|
|
346
|
-
sourceAmount: swap.sourceAmount,
|
|
347
|
-
targetAmount: swap.targetAmount,
|
|
348
|
-
exchangeRate: swap.exchangeRate,
|
|
349
|
-
createdAt: new Date(swap.createdAt),
|
|
350
|
-
completedAt: swap.completedAt ? new Date(swap.completedAt) : undefined,
|
|
351
|
-
txid: swap.txid,
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
return history.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
285
|
+
const client = await this.getClient();
|
|
286
|
+
const allSwaps = await client.listAllSwaps();
|
|
287
|
+
return allSwaps
|
|
288
|
+
.map((stored) => this.storedSwapToInfo(stored))
|
|
289
|
+
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
355
290
|
}
|
|
356
|
-
/**
|
|
357
|
-
* Get available trading pairs.
|
|
358
|
-
*/
|
|
359
291
|
async getAvailablePairs() {
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
minAmount: pair.minAmount || pair.min || 0,
|
|
369
|
-
maxAmount: pair.maxAmount || pair.max || 0,
|
|
370
|
-
feePercentage: pair.feePercentage || pair.fee || 0,
|
|
292
|
+
const client = await this.getClient();
|
|
293
|
+
const pairs = await client.getAssetPairs();
|
|
294
|
+
return pairs.map((pair) => ({
|
|
295
|
+
from: pair.source.token_id,
|
|
296
|
+
to: pair.target.token_id,
|
|
297
|
+
minAmount: 0,
|
|
298
|
+
maxAmount: 0,
|
|
299
|
+
feePercentage: 0,
|
|
371
300
|
}));
|
|
372
301
|
}
|
|
373
|
-
/**
|
|
374
|
-
* Claim funds from a completed swap.
|
|
375
|
-
*/
|
|
376
302
|
async claimSwap(swapId) {
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
const stored = this.swapStorage.get(swapId);
|
|
386
|
-
if (stored) {
|
|
387
|
-
stored.status = "completed";
|
|
388
|
-
stored.completedAt = Date.now();
|
|
389
|
-
stored.txid = data.txid;
|
|
390
|
-
}
|
|
391
|
-
return { txid: data.txid };
|
|
303
|
+
const client = await this.getClient();
|
|
304
|
+
const result = await client.claim(swapId);
|
|
305
|
+
return {
|
|
306
|
+
success: result.success,
|
|
307
|
+
message: result.message,
|
|
308
|
+
txHash: result.txHash,
|
|
309
|
+
chain: result.chain,
|
|
310
|
+
};
|
|
392
311
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
312
|
+
async refundSwap(swapId, options) {
|
|
313
|
+
const client = await this.getClient();
|
|
314
|
+
const refundOptions = options?.destinationAddress
|
|
315
|
+
? { destinationAddress: options.destinationAddress }
|
|
316
|
+
: undefined;
|
|
317
|
+
const result = await client.refundSwap(swapId, refundOptions);
|
|
318
|
+
return {
|
|
319
|
+
success: result.success,
|
|
320
|
+
message: result.message,
|
|
321
|
+
txId: result.txId,
|
|
322
|
+
refundAmount: result.refundAmount
|
|
323
|
+
? Number(result.refundAmount)
|
|
324
|
+
: undefined,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
async getEvmFundingCallData(swapId, tokenDecimals) {
|
|
328
|
+
const client = await this.getClient();
|
|
329
|
+
const data = await client.getEvmFundingCallData(swapId, tokenDecimals);
|
|
330
|
+
return {
|
|
331
|
+
approve: { to: data.approve.to, data: data.approve.data },
|
|
332
|
+
createSwap: { to: data.createSwap.to, data: data.createSwap.data },
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
async getEvmRefundCallData(swapId) {
|
|
336
|
+
const client = await this.getClient();
|
|
337
|
+
const data = await client.getEvmRefundCallData(swapId);
|
|
338
|
+
return {
|
|
339
|
+
to: data.to,
|
|
340
|
+
data: data.data,
|
|
341
|
+
timelockExpired: data.timelockExpired,
|
|
342
|
+
timelockExpiry: data.timelockExpiry,
|
|
343
|
+
};
|
|
411
344
|
}
|
|
412
345
|
/**
|
|
413
346
|
* Get the underlying wallet instance.
|
|
@@ -422,32 +355,31 @@ class LendaSwapSkill {
|
|
|
422
355
|
return TOKEN_DECIMALS[token] || 6;
|
|
423
356
|
}
|
|
424
357
|
/**
|
|
425
|
-
*
|
|
358
|
+
* Convert a StoredSwap to StablecoinSwapInfo.
|
|
426
359
|
*/
|
|
427
|
-
|
|
428
|
-
const
|
|
429
|
-
const
|
|
430
|
-
|
|
360
|
+
storedSwapToInfo(stored) {
|
|
361
|
+
const resp = stored.response;
|
|
362
|
+
const direction = resp.direction === "evm_to_btc"
|
|
363
|
+
? "stablecoin_to_btc"
|
|
364
|
+
: "btc_to_stablecoin";
|
|
365
|
+
const status = mapSwapStatus(resp.status);
|
|
366
|
+
return {
|
|
367
|
+
id: stored.swapId,
|
|
368
|
+
direction,
|
|
369
|
+
status,
|
|
370
|
+
sourceToken: resp.source_token,
|
|
371
|
+
targetToken: resp.target_token,
|
|
372
|
+
sourceAmount: resp.source_amount,
|
|
373
|
+
targetAmount: resp.target_amount,
|
|
374
|
+
exchangeRate: 0,
|
|
375
|
+
createdAt: new Date(resp.created_at),
|
|
376
|
+
completedAt: status === "completed" ? new Date() : undefined,
|
|
431
377
|
};
|
|
432
|
-
if (this.apiKey) {
|
|
433
|
-
headers["X-API-Key"] = this.apiKey;
|
|
434
|
-
}
|
|
435
|
-
return fetch(url, {
|
|
436
|
-
...options,
|
|
437
|
-
headers: {
|
|
438
|
-
...headers,
|
|
439
|
-
...options.headers,
|
|
440
|
-
},
|
|
441
|
-
});
|
|
442
378
|
}
|
|
443
379
|
}
|
|
444
380
|
exports.LendaSwapSkill = LendaSwapSkill;
|
|
445
381
|
/**
|
|
446
382
|
* Create a LendaSwapSkill from a wallet.
|
|
447
|
-
*
|
|
448
|
-
* @param wallet - The Arkade wallet to use
|
|
449
|
-
* @param options - Optional configuration
|
|
450
|
-
* @returns A new LendaSwapSkill instance
|
|
451
383
|
*/
|
|
452
384
|
function createLendaSwapSkill(wallet, options) {
|
|
453
385
|
return new LendaSwapSkill({
|