@atomiqlabs/chain-solana 9.0.0 → 10.0.0-dev.3
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/dist/solana/SolanaChains.d.ts +6 -0
- package/dist/solana/SolanaChains.js +7 -0
- package/dist/solana/chain/modules/SolanaFees.js +0 -1
- package/dist/solana/events/SolanaChainEvents.js +7 -10
- package/dist/solana/events/SolanaChainEventsBrowser.js +1 -1
- package/dist/solana/program/modules/SolanaProgramEvents.d.ts +2 -2
- package/dist/solana/program/modules/SolanaProgramEvents.js +1 -1
- package/dist/solana/swaps/SolanaSwapProgram.d.ts +9 -9
- package/dist/solana/swaps/SolanaSwapProgram.js +45 -26
- package/dist/solana/swaps/modules/SwapInit.d.ts +2 -1
- package/dist/solana/swaps/modules/SwapInit.js +14 -6
- package/dist/solana/swaps/modules/SwapRefund.js +0 -1
- package/dist/utils/Utils.js +4 -4
- package/package.json +2 -2
- package/src/solana/SolanaChains.ts +7 -0
- package/src/solana/chain/modules/SolanaFees.ts +0 -1
- package/src/solana/events/SolanaChainEvents.ts +7 -11
- package/src/solana/events/SolanaChainEventsBrowser.ts +1 -1
- package/src/solana/program/modules/SolanaProgramEvents.ts +2 -2
- package/src/solana/swaps/SolanaSwapProgram.ts +55 -32
- package/src/solana/swaps/modules/SwapInit.ts +17 -6
- package/src/solana/swaps/modules/SwapRefund.ts +0 -1
- package/src/utils/Utils.ts +4 -4
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
export declare const SolanaChains: {
|
|
2
|
+
readonly 2: {
|
|
3
|
+
readonly addresses: {
|
|
4
|
+
readonly swapContract: "11111111111111111111111111111111";
|
|
5
|
+
readonly btcRelayContract: "11111111111111111111111111111111";
|
|
6
|
+
};
|
|
7
|
+
};
|
|
2
8
|
readonly 1: {
|
|
3
9
|
readonly addresses: {
|
|
4
10
|
readonly swapContract: "4hfUykhqmD7ZRvNh1HuzVKEY7ToENixtdUKZspNDCrEM";
|
|
@@ -3,6 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.SolanaChains = void 0;
|
|
4
4
|
const base_1 = require("@atomiqlabs/base");
|
|
5
5
|
exports.SolanaChains = {
|
|
6
|
+
//TODO: Not deployed yet
|
|
7
|
+
[base_1.BitcoinNetwork.TESTNET4]: {
|
|
8
|
+
addresses: {
|
|
9
|
+
swapContract: "11111111111111111111111111111111",
|
|
10
|
+
btcRelayContract: "11111111111111111111111111111111"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
6
13
|
[base_1.BitcoinNetwork.TESTNET]: {
|
|
7
14
|
addresses: {
|
|
8
15
|
swapContract: "4hfUykhqmD7ZRvNh1HuzVKEY7ToENixtdUKZspNDCrEM",
|
|
@@ -98,13 +98,11 @@ class SolanaChainEvents extends SolanaChainEventsBrowser_1.SolanaChainEventsBrow
|
|
|
98
98
|
const eventObject = this.getEventObjectFromTransaction(transaction);
|
|
99
99
|
if (eventObject == null)
|
|
100
100
|
return true;
|
|
101
|
-
console.log("Instructions: ", eventObject.instructions);
|
|
102
|
-
console.log("Events: ", eventObject.events);
|
|
103
101
|
await this.processEvent(eventObject);
|
|
104
102
|
return true;
|
|
105
103
|
}
|
|
106
104
|
catch (e) {
|
|
107
|
-
|
|
105
|
+
this.logger.error("fetchTxAndProcessEvent(): Error fetching transaction and processing event, signature: " + signature, e);
|
|
108
106
|
return false;
|
|
109
107
|
}
|
|
110
108
|
}
|
|
@@ -121,14 +119,14 @@ class SolanaChainEvents extends SolanaChainEventsBrowser_1.SolanaChainEventsBrow
|
|
|
121
119
|
return;
|
|
122
120
|
if (this.isSignatureProcessed(signature))
|
|
123
121
|
return;
|
|
124
|
-
|
|
122
|
+
this.logger.debug("getWsEventHandler(" + name + "): Process signature: ", signature);
|
|
125
123
|
this.signaturesProcessing[signature] = this.processEvent({
|
|
126
124
|
events: [{ name, data: data }],
|
|
127
125
|
instructions: null,
|
|
128
126
|
blockTime: Math.floor(Date.now() / 1000),
|
|
129
127
|
signature
|
|
130
128
|
}).then(() => true).catch(e => {
|
|
131
|
-
|
|
129
|
+
this.logger.error("getWsEventHandler(" + name + "): Error processing signature: " + signature, e);
|
|
132
130
|
return false;
|
|
133
131
|
});
|
|
134
132
|
};
|
|
@@ -150,7 +148,7 @@ class SolanaChainEvents extends SolanaChainEventsBrowser_1.SolanaChainEventsBrow
|
|
|
150
148
|
}, "confirmed");
|
|
151
149
|
//Check if newest returned signature (index 0) is older than the latest signature's slot, this is a sanity check
|
|
152
150
|
if (fetched.length > 0 && fetched[0].slot < lastProcessedSignature.slot) {
|
|
153
|
-
|
|
151
|
+
this.logger.debug("getNewSignatures(): Sanity check triggered, returned signature slot height is older than latest!");
|
|
154
152
|
return;
|
|
155
153
|
}
|
|
156
154
|
}
|
|
@@ -198,7 +196,7 @@ class SolanaChainEvents extends SolanaChainEventsBrowser_1.SolanaChainEventsBrow
|
|
|
198
196
|
continue;
|
|
199
197
|
}
|
|
200
198
|
}
|
|
201
|
-
|
|
199
|
+
this.logger.debug("processSignatures(): Process signature: ", txSignature);
|
|
202
200
|
const processPromise = this.fetchTxAndProcessEvent(txSignature.signature);
|
|
203
201
|
this.signaturesProcessing[txSignature.signature] = processPromise;
|
|
204
202
|
const result = await processPromise;
|
|
@@ -210,7 +208,7 @@ class SolanaChainEvents extends SolanaChainEventsBrowser_1.SolanaChainEventsBrow
|
|
|
210
208
|
}
|
|
211
209
|
}
|
|
212
210
|
catch (e) {
|
|
213
|
-
|
|
211
|
+
this.logger.error("processSignatures(): Failed processing signatures: ", e);
|
|
214
212
|
}
|
|
215
213
|
return lastSuccessfulSignature;
|
|
216
214
|
}
|
|
@@ -232,8 +230,7 @@ class SolanaChainEvents extends SolanaChainEventsBrowser_1.SolanaChainEventsBrow
|
|
|
232
230
|
let func;
|
|
233
231
|
func = async () => {
|
|
234
232
|
await this.checkEvents().catch(e => {
|
|
235
|
-
|
|
236
|
-
console.error(e);
|
|
233
|
+
this.logger.error("setupHttpPolling(): Failed to fetch Solana log: ", e);
|
|
237
234
|
});
|
|
238
235
|
if (this.stopped)
|
|
239
236
|
return;
|
|
@@ -153,7 +153,7 @@ class SolanaChainEventsBrowser {
|
|
|
153
153
|
blockTime: Math.floor(Date.now() / 1000),
|
|
154
154
|
signature
|
|
155
155
|
}).then(() => true).catch(e => {
|
|
156
|
-
|
|
156
|
+
this.logger.error("wsEventHandler: Error when processing signature: " + signature, e);
|
|
157
157
|
return false;
|
|
158
158
|
});
|
|
159
159
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SolanaEvents } from "../../chain/modules/SolanaEvents";
|
|
2
2
|
import { DecodeType, Event, Idl, IdlTypes } from "@coral-xyz/anchor";
|
|
3
3
|
import { IdlField, IdlInstruction } from "@coral-xyz/anchor/dist/cjs/idl";
|
|
4
|
-
import { ParsedMessage, PublicKey } from "@solana/web3.js";
|
|
4
|
+
import { ConfirmedSignatureInfo, ParsedMessage, PublicKey } from "@solana/web3.js";
|
|
5
5
|
import { SolanaProgramBase } from "../SolanaProgramBase";
|
|
6
6
|
import { SolanaChainInterface } from "../../chain/SolanaChainInterface";
|
|
7
7
|
type DecodedFieldOrNull<D, Defined> = D extends IdlField ? DecodeType<D["type"], Defined> : unknown;
|
|
@@ -41,7 +41,7 @@ export declare class SolanaProgramEvents<IDL extends Idl> extends SolanaEvents {
|
|
|
41
41
|
* @param abortSignal
|
|
42
42
|
* @param logBatchSize how many signatures should be fetched in one getSignaturesForAddress call
|
|
43
43
|
*/
|
|
44
|
-
findInEvents<T>(topicKey: PublicKey, processor: (event: ProgramEvent<IDL
|
|
44
|
+
findInEvents<T>(topicKey: PublicKey, processor: (event: ProgramEvent<IDL>, info: ConfirmedSignatureInfo) => Promise<T>, abortSignal?: AbortSignal, logBatchSize?: number): Promise<T>;
|
|
45
45
|
/**
|
|
46
46
|
* Decodes the instructions for this program from the transaction, leaves null in the returned instructions array
|
|
47
47
|
* for every instruction that doesn't correspond to this program (as those are impossible to parse)
|
|
@@ -46,7 +46,7 @@ class SolanaProgramEvents extends SolanaEvents_1.SolanaEvents {
|
|
|
46
46
|
for (let event of await this.getEvents(data.signature)) {
|
|
47
47
|
if (abortSignal != null)
|
|
48
48
|
abortSignal.throwIfAborted();
|
|
49
|
-
const result = await processor(event);
|
|
49
|
+
const result = await processor(event, data);
|
|
50
50
|
if (result != null)
|
|
51
51
|
return result;
|
|
52
52
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { SolanaSwapData } from "./SolanaSwapData";
|
|
3
3
|
import { PublicKey } from "@solana/web3.js";
|
|
4
4
|
import { SolanaBtcRelay } from "../btcrelay/SolanaBtcRelay";
|
|
5
|
-
import { IStorageManager, SwapContract, ChainSwapType, IntermediaryReputationType,
|
|
5
|
+
import { IStorageManager, SwapContract, ChainSwapType, IntermediaryReputationType, TransactionConfirmationOptions, SignatureData, RelaySynchronizer, SwapCommitState, SwapCommitStateType } from "@atomiqlabs/base";
|
|
6
6
|
import { SolanaBtcStoredHeader } from "../btcrelay/headers/SolanaBtcStoredHeader";
|
|
7
7
|
import { SwapProgram } from "./programTypes";
|
|
8
8
|
import { SolanaChainInterface } from "../chain/SolanaChainInterface";
|
|
@@ -47,7 +47,7 @@ export declare class SolanaSwapProgram extends SolanaProgramBase<SwapProgram> im
|
|
|
47
47
|
preFetchForInitSignatureVerification(data: SolanaPreFetchData): Promise<SolanaPreFetchVerification>;
|
|
48
48
|
preFetchBlockDataForSignatures(): Promise<SolanaPreFetchData>;
|
|
49
49
|
getInitSignature(signer: SolanaSigner, swapData: SolanaSwapData, authorizationTimeout: number, preFetchedBlockData?: SolanaPreFetchData, feeRate?: string): Promise<SignatureData>;
|
|
50
|
-
isValidInitAuthorization(swapData: SolanaSwapData, { timeout, prefix, signature }: {
|
|
50
|
+
isValidInitAuthorization(signer: string, swapData: SolanaSwapData, { timeout, prefix, signature }: {
|
|
51
51
|
timeout: any;
|
|
52
52
|
prefix: any;
|
|
53
53
|
signature: any;
|
|
@@ -117,13 +117,13 @@ export declare class SolanaSwapProgram extends SolanaProgramBase<SwapProgram> im
|
|
|
117
117
|
* @param signer
|
|
118
118
|
* @param data
|
|
119
119
|
*/
|
|
120
|
-
getCommitStatus(signer: string, data: SolanaSwapData): Promise<
|
|
120
|
+
getCommitStatus(signer: string, data: SolanaSwapData): Promise<SwapCommitState>;
|
|
121
121
|
/**
|
|
122
122
|
* Checks the status of the specific payment hash
|
|
123
123
|
*
|
|
124
124
|
* @param claimHash
|
|
125
125
|
*/
|
|
126
|
-
getClaimHashStatus(claimHash: string): Promise<
|
|
126
|
+
getClaimHashStatus(claimHash: string): Promise<SwapCommitStateType>;
|
|
127
127
|
/**
|
|
128
128
|
* Returns the data committed for a specific payment hash, or null if no data is currently commited for
|
|
129
129
|
* the specific swap
|
|
@@ -155,7 +155,7 @@ export declare class SolanaSwapProgram extends SolanaProgramBase<SwapProgram> im
|
|
|
155
155
|
prefix: any;
|
|
156
156
|
signature: any;
|
|
157
157
|
}, check?: boolean, initAta?: boolean, feeRate?: string): Promise<SolanaTx[]>;
|
|
158
|
-
txsInit(swapData: SolanaSwapData, { timeout, prefix, signature }: {
|
|
158
|
+
txsInit(sender: string, swapData: SolanaSwapData, { timeout, prefix, signature }: {
|
|
159
159
|
timeout: any;
|
|
160
160
|
prefix: any;
|
|
161
161
|
signature: any;
|
|
@@ -185,18 +185,18 @@ export declare class SolanaSwapProgram extends SolanaProgramBase<SwapProgram> im
|
|
|
185
185
|
/**
|
|
186
186
|
* Get the estimated solana fee of the commit transaction
|
|
187
187
|
*/
|
|
188
|
-
getCommitFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
188
|
+
getCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
189
189
|
/**
|
|
190
190
|
* Get the estimated solana fee of the commit transaction, without any deposits
|
|
191
191
|
*/
|
|
192
|
-
getRawCommitFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
192
|
+
getRawCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
193
193
|
/**
|
|
194
194
|
* Get the estimated solana transaction fee of the refund transaction
|
|
195
195
|
*/
|
|
196
|
-
getRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
196
|
+
getRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
197
197
|
/**
|
|
198
198
|
* Get the estimated solana transaction fee of the refund transaction
|
|
199
199
|
*/
|
|
200
|
-
getRawRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
200
|
+
getRawRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
201
201
|
getExtraData(outputScript: Buffer, amount: bigint, confirmations: number, nonce?: bigint): Buffer;
|
|
202
202
|
}
|
|
@@ -67,8 +67,8 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
|
|
|
67
67
|
getInitSignature(signer, swapData, authorizationTimeout, preFetchedBlockData, feeRate) {
|
|
68
68
|
return this.Init.signSwapInitialization(signer, swapData, authorizationTimeout, preFetchedBlockData, feeRate);
|
|
69
69
|
}
|
|
70
|
-
isValidInitAuthorization(swapData, { timeout, prefix, signature }, feeRate, preFetchedData) {
|
|
71
|
-
return this.Init.isSignatureValid(swapData, timeout, prefix, signature, feeRate, preFetchedData);
|
|
70
|
+
isValidInitAuthorization(signer, swapData, { timeout, prefix, signature }, feeRate, preFetchedData) {
|
|
71
|
+
return this.Init.isSignatureValid(signer, swapData, timeout, prefix, signature, feeRate, preFetchedData);
|
|
72
72
|
}
|
|
73
73
|
getInitAuthorizationExpiry(swapData, { timeout, prefix, signature }, preFetchedData) {
|
|
74
74
|
return this.Init.getSignatureExpiry(timeout, signature, preFetchedData);
|
|
@@ -186,34 +186,49 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
|
|
|
186
186
|
if (escrowState != null) {
|
|
187
187
|
if (data.correctPDA(escrowState)) {
|
|
188
188
|
if (data.isOfferer(signer) && isExpired)
|
|
189
|
-
return base_1.
|
|
190
|
-
return base_1.
|
|
189
|
+
return { type: base_1.SwapCommitStateType.REFUNDABLE };
|
|
190
|
+
return { type: base_1.SwapCommitStateType.COMMITED };
|
|
191
191
|
}
|
|
192
192
|
if (data.isOfferer(signer) && isExpired)
|
|
193
|
-
return base_1.
|
|
194
|
-
return base_1.
|
|
193
|
+
return { type: base_1.SwapCommitStateType.EXPIRED };
|
|
194
|
+
return { type: base_1.SwapCommitStateType.NOT_COMMITED };
|
|
195
195
|
}
|
|
196
196
|
//Check if paid or what
|
|
197
|
-
const status = await this.Events.findInEvents(escrowStateKey, async (event) => {
|
|
197
|
+
const status = await this.Events.findInEvents(escrowStateKey, async (event, info) => {
|
|
198
198
|
if (event.name === "ClaimEvent") {
|
|
199
199
|
if (!event.data.sequence.eq(data.sequence))
|
|
200
200
|
return null;
|
|
201
|
-
return
|
|
201
|
+
return {
|
|
202
|
+
type: base_1.SwapCommitStateType.PAID,
|
|
203
|
+
getClaimTxId: () => Promise.resolve(info.signature),
|
|
204
|
+
getTxBlock: async () => {
|
|
205
|
+
return {
|
|
206
|
+
blockHeight: (await this.Chain.Blocks.getParsedBlock(info.slot)).blockHeight,
|
|
207
|
+
blockTime: info.blockTime
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
};
|
|
202
211
|
}
|
|
203
212
|
if (event.name === "RefundEvent") {
|
|
204
213
|
if (!event.data.sequence.eq(data.sequence))
|
|
205
214
|
return null;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
215
|
+
return {
|
|
216
|
+
type: isExpired ? base_1.SwapCommitStateType.EXPIRED : base_1.SwapCommitStateType.NOT_COMMITED,
|
|
217
|
+
getRefundTxId: () => Promise.resolve(info.signature),
|
|
218
|
+
getTxBlock: async () => {
|
|
219
|
+
return {
|
|
220
|
+
blockHeight: (await this.Chain.Blocks.getParsedBlock(info.slot)).blockHeight,
|
|
221
|
+
blockTime: info.blockTime
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
};
|
|
209
225
|
}
|
|
210
226
|
});
|
|
211
227
|
if (status != null)
|
|
212
228
|
return status;
|
|
213
|
-
if (isExpired)
|
|
214
|
-
return base_1.
|
|
215
|
-
}
|
|
216
|
-
return base_1.SwapCommitStatus.NOT_COMMITED;
|
|
229
|
+
if (isExpired)
|
|
230
|
+
return { type: base_1.SwapCommitStateType.EXPIRED };
|
|
231
|
+
return { type: base_1.SwapCommitStateType.NOT_COMMITED };
|
|
217
232
|
}
|
|
218
233
|
/**
|
|
219
234
|
* Checks the status of the specific payment hash
|
|
@@ -227,9 +242,9 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
|
|
|
227
242
|
//Start fetching events before checking escrow PDA, this call is used when quoting, so saving 100ms here helps a lot!
|
|
228
243
|
const eventsPromise = this.Events.findInEvents(escrowStateKey, async (event) => {
|
|
229
244
|
if (event.name === "ClaimEvent")
|
|
230
|
-
return base_1.
|
|
245
|
+
return base_1.SwapCommitStateType.PAID;
|
|
231
246
|
if (event.name === "RefundEvent")
|
|
232
|
-
return base_1.
|
|
247
|
+
return base_1.SwapCommitStateType.NOT_COMMITED;
|
|
233
248
|
}, abortController.signal).catch(e => {
|
|
234
249
|
abortController.abort(e);
|
|
235
250
|
return null;
|
|
@@ -238,14 +253,14 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
|
|
|
238
253
|
abortController.signal.throwIfAborted();
|
|
239
254
|
if (escrowState != null) {
|
|
240
255
|
abortController.abort();
|
|
241
|
-
return base_1.
|
|
256
|
+
return base_1.SwapCommitStateType.COMMITED;
|
|
242
257
|
}
|
|
243
258
|
//Check if paid or what
|
|
244
259
|
const eventsStatus = await eventsPromise;
|
|
245
260
|
abortController.signal.throwIfAborted();
|
|
246
261
|
if (eventsStatus != null)
|
|
247
262
|
return eventsStatus;
|
|
248
|
-
return base_1.
|
|
263
|
+
return base_1.SwapCommitStateType.NOT_COMMITED;
|
|
249
264
|
}
|
|
250
265
|
/**
|
|
251
266
|
* Returns the data committed for a specific payment hash, or null if no data is currently commited for
|
|
@@ -313,11 +328,15 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
|
|
|
313
328
|
throw new Error("Only offerer can refund on Solana");
|
|
314
329
|
return this.Refund.txsRefundWithAuthorization(swapData, timeout, prefix, signature, check, initAta, feeRate);
|
|
315
330
|
}
|
|
316
|
-
txsInit(swapData, { timeout, prefix, signature }, skipChecks, feeRate) {
|
|
331
|
+
txsInit(sender, swapData, { timeout, prefix, signature }, skipChecks, feeRate) {
|
|
317
332
|
if (swapData.isPayIn()) {
|
|
333
|
+
if (!swapData.isOfferer(sender))
|
|
334
|
+
throw new Error("Only offerer can create payIn=true swap");
|
|
318
335
|
return this.Init.txsInitPayIn(swapData, timeout, prefix, signature, skipChecks, feeRate);
|
|
319
336
|
}
|
|
320
337
|
else {
|
|
338
|
+
if (!swapData.isClaimer(sender))
|
|
339
|
+
throw new Error("Only claimer can create payIn=false swap");
|
|
321
340
|
return this.Init.txsInit(swapData, timeout, prefix, signature, skipChecks, feeRate);
|
|
322
341
|
}
|
|
323
342
|
}
|
|
@@ -367,14 +386,14 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
|
|
|
367
386
|
if (!signer.getPublicKey().equals(swapData.claimer))
|
|
368
387
|
throw new Error("Invalid signer provided!");
|
|
369
388
|
}
|
|
370
|
-
const result = await this.txsInit(swapData, signature, skipChecks, txOptions?.feeRate);
|
|
389
|
+
const result = await this.txsInit(signer.getAddress(), swapData, signature, skipChecks, txOptions?.feeRate);
|
|
371
390
|
const [txSignature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
372
391
|
return txSignature;
|
|
373
392
|
}
|
|
374
393
|
async initAndClaimWithSecret(signer, swapData, signature, secret, skipChecks, txOptions) {
|
|
375
394
|
if (!signer.getPublicKey().equals(swapData.claimer))
|
|
376
395
|
throw new Error("Invalid signer provided!");
|
|
377
|
-
const txsCommit = await this.txsInit(swapData, signature, skipChecks, txOptions?.feeRate);
|
|
396
|
+
const txsCommit = await this.txsInit(signer.getAddress(), swapData, signature, skipChecks, txOptions?.feeRate);
|
|
378
397
|
const txsClaim = await this.Claim.txsClaimWithSecret(signer.getPublicKey(), swapData, secret, true, false, txOptions?.feeRate, true);
|
|
379
398
|
return await this.Chain.sendAndConfirm(signer, txsCommit.concat(txsClaim), txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
380
399
|
}
|
|
@@ -413,25 +432,25 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
|
|
|
413
432
|
/**
|
|
414
433
|
* Get the estimated solana fee of the commit transaction
|
|
415
434
|
*/
|
|
416
|
-
getCommitFee(swapData, feeRate) {
|
|
435
|
+
getCommitFee(signer, swapData, feeRate) {
|
|
417
436
|
return this.Init.getInitFee(swapData, feeRate);
|
|
418
437
|
}
|
|
419
438
|
/**
|
|
420
439
|
* Get the estimated solana fee of the commit transaction, without any deposits
|
|
421
440
|
*/
|
|
422
|
-
getRawCommitFee(swapData, feeRate) {
|
|
441
|
+
getRawCommitFee(signer, swapData, feeRate) {
|
|
423
442
|
return this.Init.getRawInitFee(swapData, feeRate);
|
|
424
443
|
}
|
|
425
444
|
/**
|
|
426
445
|
* Get the estimated solana transaction fee of the refund transaction
|
|
427
446
|
*/
|
|
428
|
-
getRefundFee(swapData, feeRate) {
|
|
447
|
+
getRefundFee(signer, swapData, feeRate) {
|
|
429
448
|
return this.Refund.getRefundFee(swapData, feeRate);
|
|
430
449
|
}
|
|
431
450
|
/**
|
|
432
451
|
* Get the estimated solana transaction fee of the refund transaction
|
|
433
452
|
*/
|
|
434
|
-
getRawRefundFee(swapData, feeRate) {
|
|
453
|
+
getRawRefundFee(signer, swapData, feeRate) {
|
|
435
454
|
return this.Refund.getRawRefundFee(swapData, feeRate);
|
|
436
455
|
}
|
|
437
456
|
getExtraData(outputScript, amount, confirmations, nonce) {
|
|
@@ -135,6 +135,7 @@ export declare class SwapInit extends SolanaSwapModule {
|
|
|
135
135
|
/**
|
|
136
136
|
* Checks whether the provided signature data is valid, using preFetchedData if provided and still valid
|
|
137
137
|
*
|
|
138
|
+
* @param sender
|
|
138
139
|
* @param swapData
|
|
139
140
|
* @param timeout
|
|
140
141
|
* @param prefix
|
|
@@ -143,7 +144,7 @@ export declare class SwapInit extends SolanaSwapModule {
|
|
|
143
144
|
* @param preFetchedData
|
|
144
145
|
* @public
|
|
145
146
|
*/
|
|
146
|
-
isSignatureValid(swapData: SolanaSwapData, timeout: string, prefix: string, signature: string, feeRate?: string, preFetchedData?: SolanaPreFetchVerification): Promise<Buffer>;
|
|
147
|
+
isSignatureValid(sender: string, swapData: SolanaSwapData, timeout: string, prefix: string, signature: string, feeRate?: string, preFetchedData?: SolanaPreFetchVerification): Promise<Buffer>;
|
|
147
148
|
/**
|
|
148
149
|
* Gets expiry of the provided signature data, this is a minimum of slot expiry & swap signature expiry
|
|
149
150
|
*
|
|
@@ -257,6 +257,7 @@ class SwapInit extends SolanaSwapModule_1.SolanaSwapModule {
|
|
|
257
257
|
/**
|
|
258
258
|
* Checks whether the provided signature data is valid, using preFetchedData if provided and still valid
|
|
259
259
|
*
|
|
260
|
+
* @param sender
|
|
260
261
|
* @param swapData
|
|
261
262
|
* @param timeout
|
|
262
263
|
* @param prefix
|
|
@@ -265,8 +266,15 @@ class SwapInit extends SolanaSwapModule_1.SolanaSwapModule {
|
|
|
265
266
|
* @param preFetchedData
|
|
266
267
|
* @public
|
|
267
268
|
*/
|
|
268
|
-
async isSignatureValid(swapData, timeout, prefix, signature, feeRate, preFetchedData) {
|
|
269
|
-
|
|
269
|
+
async isSignatureValid(sender, swapData, timeout, prefix, signature, feeRate, preFetchedData) {
|
|
270
|
+
if (swapData.isPayIn()) {
|
|
271
|
+
if (!swapData.isOfferer(sender))
|
|
272
|
+
throw new base_1.SignatureVerificationError("Sender needs to be offerer in payIn=true swaps");
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
if (!swapData.isClaimer(sender))
|
|
276
|
+
throw new base_1.SignatureVerificationError("Sender needs to be claimer in payIn=false swaps");
|
|
277
|
+
}
|
|
270
278
|
const signer = swapData.isPayIn() ? swapData.claimer : swapData.offerer;
|
|
271
279
|
if (!swapData.isPayIn() && await this.program.isExpired(sender.toString(), swapData)) {
|
|
272
280
|
throw new base_1.SignatureVerificationError("Swap will expire too soon!");
|
|
@@ -288,7 +296,7 @@ class SwapInit extends SolanaSwapModule_1.SolanaSwapModule {
|
|
|
288
296
|
if (slotsLeft < 0)
|
|
289
297
|
throw new base_1.SignatureVerificationError("Authorization expired!");
|
|
290
298
|
const txToSign = await this.getTxToSign(swapData, timeout, feeRate);
|
|
291
|
-
txToSign.feePayer = sender;
|
|
299
|
+
txToSign.feePayer = new web3_js_1.PublicKey(sender);
|
|
292
300
|
txToSign.recentBlockhash = blockhash;
|
|
293
301
|
txToSign.addSignature(signer, buffer_1.Buffer.from(signatureString, "hex"));
|
|
294
302
|
this.logger.debug("isSignatureValid(): Signed tx: ", txToSign);
|
|
@@ -353,10 +361,10 @@ class SwapInit extends SolanaSwapModule_1.SolanaSwapModule {
|
|
|
353
361
|
async txsInitPayIn(swapData, timeout, prefix, signature, skipChecks, feeRate) {
|
|
354
362
|
if (!skipChecks) {
|
|
355
363
|
const [_, payStatus] = await Promise.all([
|
|
356
|
-
(0, Utils_1.tryWithRetries)(() => this.isSignatureValid(swapData, timeout, prefix, signature, feeRate), this.retryPolicy, (e) => e instanceof base_1.SignatureVerificationError),
|
|
364
|
+
(0, Utils_1.tryWithRetries)(() => this.isSignatureValid(swapData.getOfferer(), swapData, timeout, prefix, signature, feeRate), this.retryPolicy, (e) => e instanceof base_1.SignatureVerificationError),
|
|
357
365
|
(0, Utils_1.tryWithRetries)(() => this.program.getClaimHashStatus(swapData.getClaimHash()), this.retryPolicy)
|
|
358
366
|
]);
|
|
359
|
-
if (payStatus !== base_1.
|
|
367
|
+
if (payStatus !== base_1.SwapCommitStateType.NOT_COMMITED)
|
|
360
368
|
throw new base_1.SwapDataVerificationError("Invoice already being paid for or paid");
|
|
361
369
|
}
|
|
362
370
|
const [slotNumber, signatureStr] = signature.split(";");
|
|
@@ -393,7 +401,7 @@ class SwapInit extends SolanaSwapModule_1.SolanaSwapModule {
|
|
|
393
401
|
*/
|
|
394
402
|
async txsInit(swapData, timeout, prefix, signature, skipChecks, feeRate) {
|
|
395
403
|
if (!skipChecks) {
|
|
396
|
-
await (0, Utils_1.tryWithRetries)(() => this.isSignatureValid(swapData, timeout, prefix, signature, feeRate), this.retryPolicy, (e) => e instanceof base_1.SignatureVerificationError);
|
|
404
|
+
await (0, Utils_1.tryWithRetries)(() => this.isSignatureValid(swapData.getClaimer(), swapData, timeout, prefix, signature, feeRate), this.retryPolicy, (e) => e instanceof base_1.SignatureVerificationError);
|
|
397
405
|
}
|
|
398
406
|
const [slotNumber, signatureStr] = signature.split(";");
|
|
399
407
|
const block = await (0, Utils_1.tryWithRetries)(() => this.root.Blocks.getParsedBlock(parseInt(slotNumber)), this.retryPolicy);
|
|
@@ -185,7 +185,6 @@ class SwapRefund extends SolanaSwapModule_1.SolanaSwapModule {
|
|
|
185
185
|
throw new base_1.SwapDataVerificationError("ATA not initialized");
|
|
186
186
|
if (feeRate == null)
|
|
187
187
|
feeRate = await this.program.getRefundFeeRate(swapData);
|
|
188
|
-
console.log("[SolanaSwapProgram] txsRefundsWithAuthorization: feeRate: ", feeRate);
|
|
189
188
|
const signatureBuffer = buffer_1.Buffer.from(signature, "hex");
|
|
190
189
|
const shouldUnwrap = this.shouldUnwrap(swapData);
|
|
191
190
|
const action = await this.RefundWithSignature(swapData, timeout, prefix, signatureBuffer);
|
package/dist/utils/Utils.js
CHANGED
|
@@ -31,10 +31,10 @@ function onceAsync(executor) {
|
|
|
31
31
|
exports.onceAsync = onceAsync;
|
|
32
32
|
function getLogger(prefix) {
|
|
33
33
|
return {
|
|
34
|
-
debug: (msg, ...args) => console.debug(prefix + msg, ...args),
|
|
35
|
-
info: (msg, ...args) => console.info(prefix + msg, ...args),
|
|
36
|
-
warn: (msg, ...args) => console.warn(prefix + msg, ...args),
|
|
37
|
-
error: (msg, ...args) => console.error(prefix + msg, ...args)
|
|
34
|
+
debug: (msg, ...args) => global.atomiqLogLevel >= 3 && console.debug(prefix + msg, ...args),
|
|
35
|
+
info: (msg, ...args) => global.atomiqLogLevel >= 2 && console.info(prefix + msg, ...args),
|
|
36
|
+
warn: (msg, ...args) => (global.atomiqLogLevel == null || global.atomiqLogLevel >= 1) && console.warn(prefix + msg, ...args),
|
|
37
|
+
error: (msg, ...args) => (global.atomiqLogLevel == null || global.atomiqLogLevel >= 0) && console.error(prefix + msg, ...args)
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
exports.getLogger = getLogger;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atomiqlabs/chain-solana",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0-dev.3",
|
|
4
4
|
"description": "Solana specific base implementation",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types:": "./dist/index.d.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"author": "adambor",
|
|
23
23
|
"license": "ISC",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@atomiqlabs/base": "^
|
|
25
|
+
"@atomiqlabs/base": "^10.0.0-dev.2",
|
|
26
26
|
"@noble/hashes": "^1.7.1",
|
|
27
27
|
"bn.js": "5.2.1",
|
|
28
28
|
"bs58": "4.0.1",
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import {BitcoinNetwork} from "@atomiqlabs/base";
|
|
2
2
|
|
|
3
3
|
export const SolanaChains = {
|
|
4
|
+
//TODO: Not deployed yet
|
|
5
|
+
[BitcoinNetwork.TESTNET4]: {
|
|
6
|
+
addresses: {
|
|
7
|
+
swapContract: "11111111111111111111111111111111",
|
|
8
|
+
btcRelayContract: "11111111111111111111111111111111"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
4
11
|
[BitcoinNetwork.TESTNET]: {
|
|
5
12
|
addresses: {
|
|
6
13
|
swapContract: "4hfUykhqmD7ZRvNh1HuzVKEY7ToENixtdUKZspNDCrEM",
|
|
@@ -125,13 +125,10 @@ export class SolanaChainEvents extends SolanaChainEventsBrowser {
|
|
|
125
125
|
const eventObject = this.getEventObjectFromTransaction(transaction);
|
|
126
126
|
if(eventObject==null) return true;
|
|
127
127
|
|
|
128
|
-
console.log("Instructions: ", eventObject.instructions);
|
|
129
|
-
console.log("Events: ", eventObject.events);
|
|
130
|
-
|
|
131
128
|
await this.processEvent(eventObject);
|
|
132
129
|
return true;
|
|
133
130
|
} catch (e) {
|
|
134
|
-
|
|
131
|
+
this.logger.error("fetchTxAndProcessEvent(): Error fetching transaction and processing event, signature: "+signature, e);
|
|
135
132
|
return false;
|
|
136
133
|
}
|
|
137
134
|
}
|
|
@@ -150,7 +147,7 @@ export class SolanaChainEvents extends SolanaChainEventsBrowser {
|
|
|
150
147
|
if(this.signaturesProcessing[signature]!=null) return;
|
|
151
148
|
if(this.isSignatureProcessed(signature)) return;
|
|
152
149
|
|
|
153
|
-
|
|
150
|
+
this.logger.debug("getWsEventHandler("+name+"): Process signature: ", signature);
|
|
154
151
|
|
|
155
152
|
this.signaturesProcessing[signature] = this.processEvent({
|
|
156
153
|
events: [{name, data: data as any}],
|
|
@@ -158,7 +155,7 @@ export class SolanaChainEvents extends SolanaChainEventsBrowser {
|
|
|
158
155
|
blockTime: Math.floor(Date.now()/1000),
|
|
159
156
|
signature
|
|
160
157
|
}).then(() => true).catch(e => {
|
|
161
|
-
|
|
158
|
+
this.logger.error("getWsEventHandler("+name+"): Error processing signature: "+signature, e);
|
|
162
159
|
return false;
|
|
163
160
|
});
|
|
164
161
|
};
|
|
@@ -182,7 +179,7 @@ export class SolanaChainEvents extends SolanaChainEventsBrowser {
|
|
|
182
179
|
}, "confirmed");
|
|
183
180
|
//Check if newest returned signature (index 0) is older than the latest signature's slot, this is a sanity check
|
|
184
181
|
if(fetched.length>0 && fetched[0].slot<lastProcessedSignature.slot) {
|
|
185
|
-
|
|
182
|
+
this.logger.debug("getNewSignatures(): Sanity check triggered, returned signature slot height is older than latest!");
|
|
186
183
|
return;
|
|
187
184
|
}
|
|
188
185
|
} else {
|
|
@@ -236,7 +233,7 @@ export class SolanaChainEvents extends SolanaChainEventsBrowser {
|
|
|
236
233
|
}
|
|
237
234
|
}
|
|
238
235
|
|
|
239
|
-
|
|
236
|
+
this.logger.debug("processSignatures(): Process signature: ", txSignature);
|
|
240
237
|
|
|
241
238
|
const processPromise: Promise<boolean> = this.fetchTxAndProcessEvent(txSignature.signature);
|
|
242
239
|
this.signaturesProcessing[txSignature.signature] = processPromise;
|
|
@@ -248,7 +245,7 @@ export class SolanaChainEvents extends SolanaChainEventsBrowser {
|
|
|
248
245
|
delete this.signaturesProcessing[txSignature.signature];
|
|
249
246
|
}
|
|
250
247
|
} catch (e) {
|
|
251
|
-
|
|
248
|
+
this.logger.error("processSignatures(): Failed processing signatures: ", e);
|
|
252
249
|
}
|
|
253
250
|
return lastSuccessfulSignature;
|
|
254
251
|
}
|
|
@@ -276,8 +273,7 @@ export class SolanaChainEvents extends SolanaChainEventsBrowser {
|
|
|
276
273
|
let func;
|
|
277
274
|
func = async () => {
|
|
278
275
|
await this.checkEvents().catch(e => {
|
|
279
|
-
|
|
280
|
-
console.error(e);
|
|
276
|
+
this.logger.error("setupHttpPolling(): Failed to fetch Solana log: ", e);
|
|
281
277
|
});
|
|
282
278
|
if(this.stopped) return;
|
|
283
279
|
this.timeout = setTimeout(func, this.logFetchInterval);
|
|
@@ -211,7 +211,7 @@ export class SolanaChainEventsBrowser implements ChainEvents<SolanaSwapData> {
|
|
|
211
211
|
blockTime: Math.floor(Date.now()/1000),
|
|
212
212
|
signature
|
|
213
213
|
}).then(() => true).catch(e => {
|
|
214
|
-
|
|
214
|
+
this.logger.error("wsEventHandler: Error when processing signature: "+signature, e);
|
|
215
215
|
return false;
|
|
216
216
|
});
|
|
217
217
|
};
|
|
@@ -70,7 +70,7 @@ export class SolanaProgramEvents<IDL extends Idl> extends SolanaEvents {
|
|
|
70
70
|
*/
|
|
71
71
|
public findInEvents<T>(
|
|
72
72
|
topicKey: PublicKey,
|
|
73
|
-
processor: (event: ProgramEvent<IDL
|
|
73
|
+
processor: (event: ProgramEvent<IDL>, info: ConfirmedSignatureInfo) => Promise<T>,
|
|
74
74
|
abortSignal?: AbortSignal,
|
|
75
75
|
logBatchSize?: number
|
|
76
76
|
): Promise<T> {
|
|
@@ -78,7 +78,7 @@ export class SolanaProgramEvents<IDL extends Idl> extends SolanaEvents {
|
|
|
78
78
|
for(let data of signatures) {
|
|
79
79
|
for(let event of await this.getEvents(data.signature)) {
|
|
80
80
|
if(abortSignal!=null) abortSignal.throwIfAborted();
|
|
81
|
-
const result: T = await processor(event);
|
|
81
|
+
const result: T = await processor(event, data);
|
|
82
82
|
if(result!=null) return result;
|
|
83
83
|
}
|
|
84
84
|
}
|
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
import {SolanaSwapData} from "./SolanaSwapData";
|
|
2
2
|
import {IdlAccounts} from "@coral-xyz/anchor";
|
|
3
3
|
import {
|
|
4
|
-
Connection,
|
|
5
4
|
PublicKey,
|
|
6
5
|
} from "@solana/web3.js";
|
|
7
6
|
import {sha256} from "@noble/hashes/sha2";
|
|
8
7
|
import {SolanaBtcRelay} from "../btcrelay/SolanaBtcRelay";
|
|
9
8
|
import * as programIdl from "./programIdl.json";
|
|
10
9
|
import {
|
|
11
|
-
IStorageManager,
|
|
12
|
-
|
|
10
|
+
IStorageManager,
|
|
11
|
+
SwapContract,
|
|
12
|
+
ChainSwapType,
|
|
13
|
+
IntermediaryReputationType,
|
|
14
|
+
TransactionConfirmationOptions,
|
|
15
|
+
SignatureData,
|
|
16
|
+
RelaySynchronizer,
|
|
17
|
+
BigIntBufferUtils,
|
|
18
|
+
SwapCommitState,
|
|
19
|
+
SwapCommitStateType, SwapNotCommitedState, SwapExpiredState, SwapPaidState
|
|
13
20
|
} from "@atomiqlabs/base";
|
|
14
21
|
import {SolanaBtcStoredHeader} from "../btcrelay/headers/SolanaBtcStoredHeader";
|
|
15
22
|
import {
|
|
16
23
|
getAssociatedTokenAddressSync,
|
|
17
24
|
} from "@solana/spl-token";
|
|
18
|
-
import {SolanaFees} from "../chain/modules/SolanaFees";
|
|
19
25
|
import {SwapProgram} from "./programTypes";
|
|
20
|
-
import {SolanaChainInterface
|
|
26
|
+
import {SolanaChainInterface} from "../chain/SolanaChainInterface";
|
|
21
27
|
import {SolanaProgramBase} from "../program/SolanaProgramBase";
|
|
22
28
|
import {SolanaTx} from "../chain/modules/SolanaTransactions";
|
|
23
29
|
import {SwapInit, SolanaPreFetchData, SolanaPreFetchVerification} from "./modules/SwapInit";
|
|
@@ -118,8 +124,8 @@ export class SolanaSwapProgram
|
|
|
118
124
|
return this.Init.signSwapInitialization(signer, swapData, authorizationTimeout, preFetchedBlockData, feeRate);
|
|
119
125
|
}
|
|
120
126
|
|
|
121
|
-
isValidInitAuthorization(swapData: SolanaSwapData, {timeout, prefix, signature}, feeRate?: string, preFetchedData?: SolanaPreFetchVerification): Promise<Buffer> {
|
|
122
|
-
return this.Init.isSignatureValid(swapData, timeout, prefix, signature, feeRate, preFetchedData);
|
|
127
|
+
isValidInitAuthorization(signer: string, swapData: SolanaSwapData, {timeout, prefix, signature}, feeRate?: string, preFetchedData?: SolanaPreFetchVerification): Promise<Buffer> {
|
|
128
|
+
return this.Init.isSignatureValid(signer, swapData, timeout, prefix, signature, feeRate, preFetchedData);
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
getInitAuthorizationExpiry(swapData: SolanaSwapData, {timeout, prefix, signature}, preFetchedData?: SolanaPreFetchVerification): Promise<number> {
|
|
@@ -245,7 +251,7 @@ export class SolanaSwapProgram
|
|
|
245
251
|
* @param signer
|
|
246
252
|
* @param data
|
|
247
253
|
*/
|
|
248
|
-
async getCommitStatus(signer: string, data: SolanaSwapData): Promise<
|
|
254
|
+
async getCommitStatus(signer: string, data: SolanaSwapData): Promise<SwapCommitState> {
|
|
249
255
|
const escrowStateKey = this.SwapEscrowState(Buffer.from(data.paymentHash, "hex"));
|
|
250
256
|
const [escrowState, isExpired] = await Promise.all([
|
|
251
257
|
this.program.account.escrowState.fetchNullable(escrowStateKey) as Promise<IdlAccounts<SwapProgram>["escrowState"]>,
|
|
@@ -254,32 +260,47 @@ export class SolanaSwapProgram
|
|
|
254
260
|
|
|
255
261
|
if(escrowState!=null) {
|
|
256
262
|
if(data.correctPDA(escrowState)) {
|
|
257
|
-
if(data.isOfferer(signer) && isExpired) return
|
|
258
|
-
return
|
|
263
|
+
if(data.isOfferer(signer) && isExpired) return {type: SwapCommitStateType.REFUNDABLE};
|
|
264
|
+
return {type: SwapCommitStateType.COMMITED};
|
|
259
265
|
}
|
|
260
266
|
|
|
261
|
-
if(data.isOfferer(signer) && isExpired) return
|
|
262
|
-
return
|
|
267
|
+
if(data.isOfferer(signer) && isExpired) return {type: SwapCommitStateType.EXPIRED};
|
|
268
|
+
return {type: SwapCommitStateType.NOT_COMMITED};
|
|
263
269
|
}
|
|
264
270
|
|
|
265
271
|
//Check if paid or what
|
|
266
|
-
const status = await this.Events.findInEvents(escrowStateKey, async (event) => {
|
|
272
|
+
const status: SwapNotCommitedState | SwapExpiredState | SwapPaidState = await this.Events.findInEvents(escrowStateKey, async (event, info) => {
|
|
267
273
|
if(event.name==="ClaimEvent") {
|
|
268
274
|
if(!event.data.sequence.eq(data.sequence)) return null;
|
|
269
|
-
return
|
|
275
|
+
return {
|
|
276
|
+
type: SwapCommitStateType.PAID,
|
|
277
|
+
getClaimTxId: () => Promise.resolve(info.signature),
|
|
278
|
+
getTxBlock: async () => {
|
|
279
|
+
return {
|
|
280
|
+
blockHeight: (await this.Chain.Blocks.getParsedBlock(info.slot)).blockHeight,
|
|
281
|
+
blockTime: info.blockTime
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
}
|
|
270
285
|
}
|
|
271
286
|
if(event.name==="RefundEvent") {
|
|
272
287
|
if(!event.data.sequence.eq(data.sequence)) return null;
|
|
273
|
-
|
|
274
|
-
|
|
288
|
+
return {
|
|
289
|
+
type: isExpired ? SwapCommitStateType.EXPIRED : SwapCommitStateType.NOT_COMMITED,
|
|
290
|
+
getRefundTxId: () => Promise.resolve(info.signature),
|
|
291
|
+
getTxBlock: async () => {
|
|
292
|
+
return {
|
|
293
|
+
blockHeight: (await this.Chain.Blocks.getParsedBlock(info.slot)).blockHeight,
|
|
294
|
+
blockTime: info.blockTime
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
};
|
|
275
298
|
}
|
|
276
299
|
});
|
|
277
300
|
if(status!=null) return status;
|
|
278
301
|
|
|
279
|
-
if(isExpired) {
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
return SwapCommitStatus.NOT_COMMITED;
|
|
302
|
+
if(isExpired) return {type: SwapCommitStateType.EXPIRED};
|
|
303
|
+
return {type: SwapCommitStateType.NOT_COMMITED};
|
|
283
304
|
}
|
|
284
305
|
|
|
285
306
|
/**
|
|
@@ -287,15 +308,15 @@ export class SolanaSwapProgram
|
|
|
287
308
|
*
|
|
288
309
|
* @param claimHash
|
|
289
310
|
*/
|
|
290
|
-
async getClaimHashStatus(claimHash: string): Promise<
|
|
311
|
+
async getClaimHashStatus(claimHash: string): Promise<SwapCommitStateType> {
|
|
291
312
|
const {paymentHash} = fromClaimHash(claimHash);
|
|
292
313
|
const escrowStateKey = this.SwapEscrowState(Buffer.from(paymentHash, "hex"));
|
|
293
314
|
const abortController = new AbortController();
|
|
294
315
|
|
|
295
316
|
//Start fetching events before checking escrow PDA, this call is used when quoting, so saving 100ms here helps a lot!
|
|
296
317
|
const eventsPromise = this.Events.findInEvents(escrowStateKey, async (event) => {
|
|
297
|
-
if(event.name==="ClaimEvent") return
|
|
298
|
-
if(event.name==="RefundEvent") return
|
|
318
|
+
if(event.name==="ClaimEvent") return SwapCommitStateType.PAID;
|
|
319
|
+
if(event.name==="RefundEvent") return SwapCommitStateType.NOT_COMMITED;
|
|
299
320
|
}, abortController.signal).catch(e => {
|
|
300
321
|
abortController.abort(e)
|
|
301
322
|
return null;
|
|
@@ -305,7 +326,7 @@ export class SolanaSwapProgram
|
|
|
305
326
|
abortController.signal.throwIfAborted();
|
|
306
327
|
if(escrowState!=null) {
|
|
307
328
|
abortController.abort();
|
|
308
|
-
return
|
|
329
|
+
return SwapCommitStateType.COMMITED;
|
|
309
330
|
}
|
|
310
331
|
|
|
311
332
|
//Check if paid or what
|
|
@@ -313,7 +334,7 @@ export class SolanaSwapProgram
|
|
|
313
334
|
abortController.signal.throwIfAborted();
|
|
314
335
|
if(eventsStatus!=null) return eventsStatus;
|
|
315
336
|
|
|
316
|
-
return
|
|
337
|
+
return SwapCommitStateType.NOT_COMMITED;
|
|
317
338
|
}
|
|
318
339
|
|
|
319
340
|
/**
|
|
@@ -445,10 +466,12 @@ export class SolanaSwapProgram
|
|
|
445
466
|
return this.Refund.txsRefundWithAuthorization(swapData,timeout,prefix,signature,check,initAta,feeRate);
|
|
446
467
|
}
|
|
447
468
|
|
|
448
|
-
txsInit(swapData: SolanaSwapData, {timeout, prefix, signature}, skipChecks?: boolean, feeRate?: string): Promise<SolanaTx[]> {
|
|
469
|
+
txsInit(sender: string, swapData: SolanaSwapData, {timeout, prefix, signature}, skipChecks?: boolean, feeRate?: string): Promise<SolanaTx[]> {
|
|
449
470
|
if(swapData.isPayIn()) {
|
|
471
|
+
if(!swapData.isOfferer(sender)) throw new Error("Only offerer can create payIn=true swap");
|
|
450
472
|
return this.Init.txsInitPayIn(swapData, timeout, prefix, signature, skipChecks, feeRate);
|
|
451
473
|
} else {
|
|
474
|
+
if(!swapData.isClaimer(sender)) throw new Error("Only claimer can create payIn=false swap");
|
|
452
475
|
return this.Init.txsInit(swapData, timeout, prefix, signature, skipChecks, feeRate);
|
|
453
476
|
}
|
|
454
477
|
}
|
|
@@ -548,7 +571,7 @@ export class SolanaSwapProgram
|
|
|
548
571
|
if(!signer.getPublicKey().equals(swapData.claimer)) throw new Error("Invalid signer provided!");
|
|
549
572
|
}
|
|
550
573
|
|
|
551
|
-
const result = await this.txsInit(swapData, signature, skipChecks, txOptions?.feeRate);
|
|
574
|
+
const result = await this.txsInit(signer.getAddress(), swapData, signature, skipChecks, txOptions?.feeRate);
|
|
552
575
|
|
|
553
576
|
const [txSignature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
554
577
|
|
|
@@ -565,7 +588,7 @@ export class SolanaSwapProgram
|
|
|
565
588
|
): Promise<string[]> {
|
|
566
589
|
if(!signer.getPublicKey().equals(swapData.claimer)) throw new Error("Invalid signer provided!");
|
|
567
590
|
|
|
568
|
-
const txsCommit = await this.txsInit(swapData, signature, skipChecks, txOptions?.feeRate);
|
|
591
|
+
const txsCommit = await this.txsInit(signer.getAddress(), swapData, signature, skipChecks, txOptions?.feeRate);
|
|
569
592
|
const txsClaim = await this.Claim.txsClaimWithSecret(signer.getPublicKey(), swapData, secret, true, false, txOptions?.feeRate, true);
|
|
570
593
|
|
|
571
594
|
return await this.Chain.sendAndConfirm(signer, txsCommit.concat(txsClaim), txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
@@ -634,28 +657,28 @@ export class SolanaSwapProgram
|
|
|
634
657
|
/**
|
|
635
658
|
* Get the estimated solana fee of the commit transaction
|
|
636
659
|
*/
|
|
637
|
-
getCommitFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
660
|
+
getCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
638
661
|
return this.Init.getInitFee(swapData, feeRate);
|
|
639
662
|
}
|
|
640
663
|
|
|
641
664
|
/**
|
|
642
665
|
* Get the estimated solana fee of the commit transaction, without any deposits
|
|
643
666
|
*/
|
|
644
|
-
getRawCommitFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
667
|
+
getRawCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
645
668
|
return this.Init.getRawInitFee(swapData, feeRate);
|
|
646
669
|
}
|
|
647
670
|
|
|
648
671
|
/**
|
|
649
672
|
* Get the estimated solana transaction fee of the refund transaction
|
|
650
673
|
*/
|
|
651
|
-
getRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
674
|
+
getRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
652
675
|
return this.Refund.getRefundFee(swapData, feeRate);
|
|
653
676
|
}
|
|
654
677
|
|
|
655
678
|
/**
|
|
656
679
|
* Get the estimated solana transaction fee of the refund transaction
|
|
657
680
|
*/
|
|
658
|
-
getRawRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
681
|
+
getRawRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
659
682
|
return this.Refund.getRawRefundFee(swapData, feeRate);
|
|
660
683
|
}
|
|
661
684
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import {ParsedAccountsModeBlockResponse, PublicKey, SystemProgram, Transaction} from "@solana/web3.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
SignatureVerificationError,
|
|
4
|
+
SwapCommitStateType,
|
|
5
|
+
SwapDataVerificationError
|
|
6
|
+
} from "@atomiqlabs/base";
|
|
3
7
|
import {SolanaSwapData} from "../SolanaSwapData";
|
|
4
8
|
import {SolanaAction} from "../../chain/SolanaAction";
|
|
5
9
|
import {
|
|
@@ -335,6 +339,7 @@ export class SwapInit extends SolanaSwapModule {
|
|
|
335
339
|
/**
|
|
336
340
|
* Checks whether the provided signature data is valid, using preFetchedData if provided and still valid
|
|
337
341
|
*
|
|
342
|
+
* @param sender
|
|
338
343
|
* @param swapData
|
|
339
344
|
* @param timeout
|
|
340
345
|
* @param prefix
|
|
@@ -344,6 +349,7 @@ export class SwapInit extends SolanaSwapModule {
|
|
|
344
349
|
* @public
|
|
345
350
|
*/
|
|
346
351
|
public async isSignatureValid(
|
|
352
|
+
sender: string,
|
|
347
353
|
swapData: SolanaSwapData,
|
|
348
354
|
timeout: string,
|
|
349
355
|
prefix: string,
|
|
@@ -351,7 +357,12 @@ export class SwapInit extends SolanaSwapModule {
|
|
|
351
357
|
feeRate?: string,
|
|
352
358
|
preFetchedData?: SolanaPreFetchVerification
|
|
353
359
|
): Promise<Buffer> {
|
|
354
|
-
|
|
360
|
+
if(swapData.isPayIn()) {
|
|
361
|
+
if(!swapData.isOfferer(sender)) throw new SignatureVerificationError("Sender needs to be offerer in payIn=true swaps");
|
|
362
|
+
} else {
|
|
363
|
+
if(!swapData.isClaimer(sender)) throw new SignatureVerificationError("Sender needs to be claimer in payIn=false swaps");
|
|
364
|
+
}
|
|
365
|
+
|
|
355
366
|
const signer = swapData.isPayIn() ? swapData.claimer : swapData.offerer;
|
|
356
367
|
|
|
357
368
|
if(!swapData.isPayIn() && await this.program.isExpired(sender.toString(), swapData)) {
|
|
@@ -377,7 +388,7 @@ export class SwapInit extends SolanaSwapModule {
|
|
|
377
388
|
if(slotsLeft<0) throw new SignatureVerificationError("Authorization expired!");
|
|
378
389
|
|
|
379
390
|
const txToSign = await this.getTxToSign(swapData, timeout, feeRate);
|
|
380
|
-
txToSign.feePayer = sender;
|
|
391
|
+
txToSign.feePayer = new PublicKey(sender);
|
|
381
392
|
txToSign.recentBlockhash = blockhash;
|
|
382
393
|
txToSign.addSignature(signer, Buffer.from(signatureString, "hex"));
|
|
383
394
|
this.logger.debug("isSignatureValid(): Signed tx: ",txToSign);
|
|
@@ -466,12 +477,12 @@ export class SwapInit extends SolanaSwapModule {
|
|
|
466
477
|
if(!skipChecks) {
|
|
467
478
|
const [_, payStatus] = await Promise.all([
|
|
468
479
|
tryWithRetries(
|
|
469
|
-
() => this.isSignatureValid(swapData, timeout, prefix, signature, feeRate),
|
|
480
|
+
() => this.isSignatureValid(swapData.getOfferer(), swapData, timeout, prefix, signature, feeRate),
|
|
470
481
|
this.retryPolicy, (e) => e instanceof SignatureVerificationError
|
|
471
482
|
),
|
|
472
483
|
tryWithRetries(() => this.program.getClaimHashStatus(swapData.getClaimHash()), this.retryPolicy)
|
|
473
484
|
]);
|
|
474
|
-
if(payStatus!==
|
|
485
|
+
if(payStatus!==SwapCommitStateType.NOT_COMMITED) throw new SwapDataVerificationError("Invoice already being paid for or paid");
|
|
475
486
|
}
|
|
476
487
|
|
|
477
488
|
const [slotNumber, signatureStr] = signature.split(";");
|
|
@@ -522,7 +533,7 @@ export class SwapInit extends SolanaSwapModule {
|
|
|
522
533
|
public async txsInit(swapData: SolanaSwapData, timeout: string, prefix: string, signature: string, skipChecks?: boolean, feeRate?: string): Promise<SolanaTx[]> {
|
|
523
534
|
if(!skipChecks) {
|
|
524
535
|
await tryWithRetries(
|
|
525
|
-
() => this.isSignatureValid(swapData, timeout, prefix, signature, feeRate),
|
|
536
|
+
() => this.isSignatureValid(swapData.getClaimer(), swapData, timeout, prefix, signature, feeRate),
|
|
526
537
|
this.retryPolicy,
|
|
527
538
|
(e) => e instanceof SignatureVerificationError
|
|
528
539
|
);
|
|
@@ -249,7 +249,6 @@ export class SwapRefund extends SolanaSwapModule {
|
|
|
249
249
|
if(shouldInitAta && !initAta) throw new SwapDataVerificationError("ATA not initialized");
|
|
250
250
|
|
|
251
251
|
if(feeRate==null) feeRate = await this.program.getRefundFeeRate(swapData);
|
|
252
|
-
console.log("[SolanaSwapProgram] txsRefundsWithAuthorization: feeRate: ", feeRate);
|
|
253
252
|
|
|
254
253
|
const signatureBuffer = Buffer.from(signature, "hex");
|
|
255
254
|
|
package/src/utils/Utils.ts
CHANGED
|
@@ -28,10 +28,10 @@ export function onceAsync<T>(executor: () => Promise<T>): () => Promise<T> {
|
|
|
28
28
|
|
|
29
29
|
export function getLogger(prefix: string) {
|
|
30
30
|
return {
|
|
31
|
-
debug: (msg, ...args) => console.debug(prefix+msg, ...args),
|
|
32
|
-
info: (msg, ...args) => console.info(prefix+msg, ...args),
|
|
33
|
-
warn: (msg, ...args) => console.warn(prefix+msg, ...args),
|
|
34
|
-
error: (msg, ...args) => console.error(prefix+msg, ...args)
|
|
31
|
+
debug: (msg, ...args) => global.atomiqLogLevel >= 3 && console.debug(prefix+msg, ...args),
|
|
32
|
+
info: (msg, ...args) => global.atomiqLogLevel >= 2 && console.info(prefix+msg, ...args),
|
|
33
|
+
warn: (msg, ...args) => (global.atomiqLogLevel==null || global.atomiqLogLevel >= 1) && console.warn(prefix+msg, ...args),
|
|
34
|
+
error: (msg, ...args) => (global.atomiqLogLevel==null || global.atomiqLogLevel >= 0) && console.error(prefix+msg, ...args)
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
|