@atomiqlabs/chain-solana 7.2.0
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/LICENSE +201 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +44 -0
- package/dist/solana/SolanaChainType.d.ts +9 -0
- package/dist/solana/SolanaChainType.js +2 -0
- package/dist/solana/base/SolanaAction.d.ts +26 -0
- package/dist/solana/base/SolanaAction.js +99 -0
- package/dist/solana/base/SolanaBase.d.ts +36 -0
- package/dist/solana/base/SolanaBase.js +30 -0
- package/dist/solana/base/SolanaModule.d.ts +14 -0
- package/dist/solana/base/SolanaModule.js +13 -0
- package/dist/solana/base/modules/SolanaAddresses.d.ts +9 -0
- package/dist/solana/base/modules/SolanaAddresses.js +23 -0
- package/dist/solana/base/modules/SolanaBlocks.d.ts +28 -0
- package/dist/solana/base/modules/SolanaBlocks.js +83 -0
- package/dist/solana/base/modules/SolanaEvents.d.ts +25 -0
- package/dist/solana/base/modules/SolanaEvents.js +69 -0
- package/dist/solana/base/modules/SolanaFees.d.ts +121 -0
- package/dist/solana/base/modules/SolanaFees.js +393 -0
- package/dist/solana/base/modules/SolanaSignatures.d.ts +23 -0
- package/dist/solana/base/modules/SolanaSignatures.js +39 -0
- package/dist/solana/base/modules/SolanaSlots.d.ts +31 -0
- package/dist/solana/base/modules/SolanaSlots.js +81 -0
- package/dist/solana/base/modules/SolanaTokens.d.ts +134 -0
- package/dist/solana/base/modules/SolanaTokens.js +269 -0
- package/dist/solana/base/modules/SolanaTransactions.d.ts +124 -0
- package/dist/solana/base/modules/SolanaTransactions.js +354 -0
- package/dist/solana/btcrelay/SolanaBtcRelay.d.ts +229 -0
- package/dist/solana/btcrelay/SolanaBtcRelay.js +477 -0
- package/dist/solana/btcrelay/headers/SolanaBtcHeader.d.ts +29 -0
- package/dist/solana/btcrelay/headers/SolanaBtcHeader.js +34 -0
- package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.d.ts +46 -0
- package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.js +78 -0
- package/dist/solana/btcrelay/program/programIdl.json +671 -0
- package/dist/solana/events/SolanaChainEvents.d.ts +84 -0
- package/dist/solana/events/SolanaChainEvents.js +268 -0
- package/dist/solana/events/SolanaChainEventsBrowser.d.ts +85 -0
- package/dist/solana/events/SolanaChainEventsBrowser.js +202 -0
- package/dist/solana/program/SolanaProgramBase.d.ts +34 -0
- package/dist/solana/program/SolanaProgramBase.js +43 -0
- package/dist/solana/program/modules/SolanaProgramEvents.d.ts +58 -0
- package/dist/solana/program/modules/SolanaProgramEvents.js +114 -0
- package/dist/solana/swaps/SolanaSwapData.d.ts +55 -0
- package/dist/solana/swaps/SolanaSwapData.js +251 -0
- package/dist/solana/swaps/SolanaSwapModule.d.ts +9 -0
- package/dist/solana/swaps/SolanaSwapModule.js +12 -0
- package/dist/solana/swaps/SolanaSwapProgram.d.ts +218 -0
- package/dist/solana/swaps/SolanaSwapProgram.js +523 -0
- package/dist/solana/swaps/SwapTypeEnum.d.ts +11 -0
- package/dist/solana/swaps/SwapTypeEnum.js +42 -0
- package/dist/solana/swaps/modules/SolanaDataAccount.d.ts +94 -0
- package/dist/solana/swaps/modules/SolanaDataAccount.js +255 -0
- package/dist/solana/swaps/modules/SolanaLpVault.d.ts +72 -0
- package/dist/solana/swaps/modules/SolanaLpVault.js +196 -0
- package/dist/solana/swaps/modules/SwapClaim.d.ts +129 -0
- package/dist/solana/swaps/modules/SwapClaim.js +307 -0
- package/dist/solana/swaps/modules/SwapInit.d.ts +212 -0
- package/dist/solana/swaps/modules/SwapInit.js +508 -0
- package/dist/solana/swaps/modules/SwapRefund.d.ts +83 -0
- package/dist/solana/swaps/modules/SwapRefund.js +264 -0
- package/dist/solana/swaps/programIdl.json +945 -0
- package/dist/solana/swaps/programTypes.d.ts +943 -0
- package/dist/solana/swaps/programTypes.js +945 -0
- package/dist/solana/wallet/SolanaKeypairWallet.d.ts +9 -0
- package/dist/solana/wallet/SolanaKeypairWallet.js +33 -0
- package/dist/solana/wallet/SolanaSigner.d.ts +10 -0
- package/dist/solana/wallet/SolanaSigner.js +16 -0
- package/dist/utils/Utils.d.ts +43 -0
- package/dist/utils/Utils.js +143 -0
- package/package.json +40 -0
- package/src/index.ts +35 -0
- package/src/solana/SolanaChainType.ts +20 -0
- package/src/solana/base/SolanaAction.ts +109 -0
- package/src/solana/base/SolanaBase.ts +57 -0
- package/src/solana/base/SolanaModule.ts +21 -0
- package/src/solana/base/modules/SolanaAddresses.ts +22 -0
- package/src/solana/base/modules/SolanaBlocks.ts +79 -0
- package/src/solana/base/modules/SolanaEvents.ts +58 -0
- package/src/solana/base/modules/SolanaFees.ts +445 -0
- package/src/solana/base/modules/SolanaSignatures.ts +40 -0
- package/src/solana/base/modules/SolanaSlots.ts +83 -0
- package/src/solana/base/modules/SolanaTokens.ts +310 -0
- package/src/solana/base/modules/SolanaTransactions.ts +366 -0
- package/src/solana/btcrelay/SolanaBtcRelay.ts +591 -0
- package/src/solana/btcrelay/headers/SolanaBtcHeader.ts +58 -0
- package/src/solana/btcrelay/headers/SolanaBtcStoredHeader.ts +103 -0
- package/src/solana/btcrelay/program/programIdl.json +671 -0
- package/src/solana/events/SolanaChainEvents.ts +286 -0
- package/src/solana/events/SolanaChainEventsBrowser.ts +251 -0
- package/src/solana/program/SolanaProgramBase.ts +77 -0
- package/src/solana/program/modules/SolanaProgramEvents.ts +140 -0
- package/src/solana/swaps/SolanaSwapData.ts +360 -0
- package/src/solana/swaps/SolanaSwapModule.ts +17 -0
- package/src/solana/swaps/SolanaSwapProgram.ts +739 -0
- package/src/solana/swaps/SwapTypeEnum.ts +30 -0
- package/src/solana/swaps/modules/SolanaDataAccount.ts +309 -0
- package/src/solana/swaps/modules/SolanaLpVault.ts +216 -0
- package/src/solana/swaps/modules/SwapClaim.ts +397 -0
- package/src/solana/swaps/modules/SwapInit.ts +621 -0
- package/src/solana/swaps/modules/SwapRefund.ts +316 -0
- package/src/solana/swaps/programIdl.json +945 -0
- package/src/solana/swaps/programTypes.ts +1885 -0
- package/src/solana/wallet/SolanaKeypairWallet.ts +36 -0
- package/src/solana/wallet/SolanaSigner.ts +23 -0
- package/src/utils/Utils.ts +145 -0
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SwapInit = void 0;
|
|
13
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
14
|
+
const base_1 = require("@atomiqlabs/base");
|
|
15
|
+
const BN = require("bn.js");
|
|
16
|
+
const SolanaAction_1 = require("../../base/SolanaAction");
|
|
17
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
18
|
+
const SolanaSwapModule_1 = require("../SolanaSwapModule");
|
|
19
|
+
const Utils_1 = require("../../../utils/Utils");
|
|
20
|
+
const buffer_1 = require("buffer");
|
|
21
|
+
class SwapInit extends SolanaSwapModule_1.SolanaSwapModule {
|
|
22
|
+
constructor() {
|
|
23
|
+
super(...arguments);
|
|
24
|
+
this.SIGNATURE_SLOT_BUFFER = 20;
|
|
25
|
+
this.SIGNATURE_PREFETCH_DATA_VALIDITY = 5000;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* bare Init action based on the data passed in swapData
|
|
29
|
+
*
|
|
30
|
+
* @param swapData
|
|
31
|
+
* @param timeout
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
Init(swapData, timeout) {
|
|
35
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
const claimerAta = (0, spl_token_1.getAssociatedTokenAddressSync)(swapData.token, swapData.claimer);
|
|
37
|
+
const paymentHash = buffer_1.Buffer.from(swapData.paymentHash, "hex");
|
|
38
|
+
const accounts = {
|
|
39
|
+
claimer: swapData.claimer,
|
|
40
|
+
offerer: swapData.offerer,
|
|
41
|
+
escrowState: this.root.SwapEscrowState(paymentHash),
|
|
42
|
+
mint: swapData.token,
|
|
43
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
44
|
+
claimerAta: swapData.payOut ? claimerAta : null,
|
|
45
|
+
claimerUserData: !swapData.payOut ? this.root.SwapUserVault(swapData.claimer, swapData.token) : null
|
|
46
|
+
};
|
|
47
|
+
if (swapData.payIn) {
|
|
48
|
+
const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(swapData.token, swapData.offerer);
|
|
49
|
+
return new SolanaAction_1.SolanaAction(swapData.offerer, this.root, yield this.program.methods
|
|
50
|
+
.offererInitializePayIn(swapData.toSwapDataStruct(), [...buffer_1.Buffer.alloc(32, 0)], timeout)
|
|
51
|
+
.accounts(Object.assign(Object.assign({}, accounts), { offererAta: ata, vault: this.root.SwapVault(swapData.token), vaultAuthority: this.root.SwapVaultAuthority, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID }))
|
|
52
|
+
.instruction(), SwapInit.CUCosts.INIT_PAY_IN);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
return new SolanaAction_1.SolanaAction(swapData.claimer, this.root, yield this.program.methods
|
|
56
|
+
.offererInitialize(swapData.toSwapDataStruct(), swapData.securityDeposit, swapData.claimerBounty, [...(swapData.txoHash != null ? buffer_1.Buffer.from(swapData.txoHash, "hex") : buffer_1.Buffer.alloc(32, 0))], new BN(timeout))
|
|
57
|
+
.accounts(Object.assign(Object.assign({}, accounts), { offererUserData: this.root.SwapUserVault(swapData.offerer, swapData.token) }))
|
|
58
|
+
.instruction(), SwapInit.CUCosts.INIT);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* InitPayIn action which includes SOL to WSOL wrapping if indicated by the fee rate
|
|
64
|
+
*
|
|
65
|
+
* @param swapData
|
|
66
|
+
* @param timeout
|
|
67
|
+
* @param feeRate
|
|
68
|
+
* @constructor
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
InitPayIn(swapData, timeout, feeRate) {
|
|
72
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
if (!swapData.isPayIn())
|
|
74
|
+
throw new Error("Must be payIn==true");
|
|
75
|
+
const action = new SolanaAction_1.SolanaAction(swapData.offerer, this.root);
|
|
76
|
+
if (this.shouldWrapOnInit(swapData, feeRate))
|
|
77
|
+
action.addAction(this.Wrap(swapData, feeRate));
|
|
78
|
+
action.addAction(yield this.Init(swapData, timeout));
|
|
79
|
+
return action;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* InitNotPayIn action with additional createAssociatedTokenAccountIdempotentInstruction instruction, such that
|
|
84
|
+
* a recipient ATA is created if it doesn't exist
|
|
85
|
+
*
|
|
86
|
+
* @param swapData
|
|
87
|
+
* @param timeout
|
|
88
|
+
* @constructor
|
|
89
|
+
* @private
|
|
90
|
+
*/
|
|
91
|
+
InitNotPayIn(swapData, timeout) {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
if (swapData.isPayIn())
|
|
94
|
+
throw new Error("Must be payIn==false");
|
|
95
|
+
const action = new SolanaAction_1.SolanaAction(swapData.claimer, this.root);
|
|
96
|
+
action.addIx((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(swapData.claimer, swapData.claimerAta, swapData.claimer, swapData.token));
|
|
97
|
+
action.addAction(yield this.Init(swapData, timeout));
|
|
98
|
+
return action;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
Wrap(swapData, feeRate) {
|
|
102
|
+
const data = this.extractAtaDataFromFeeRate(feeRate);
|
|
103
|
+
if (data == null)
|
|
104
|
+
throw new Error("Tried to add wrap instruction, but feeRate malformed: " + feeRate);
|
|
105
|
+
return this.root.Tokens.Wrap(swapData.offerer, swapData.amount.sub(data.balance), data.initAta);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Extracts data about SOL to WSOL wrapping from the fee rate, fee rate is used to convey this information from
|
|
109
|
+
* the user to the intermediary, such that the intermediary creates valid signature for transaction including
|
|
110
|
+
* the SOL to WSOL wrapping instructions
|
|
111
|
+
*
|
|
112
|
+
* @param feeRate
|
|
113
|
+
* @private
|
|
114
|
+
*/
|
|
115
|
+
extractAtaDataFromFeeRate(feeRate) {
|
|
116
|
+
const hashArr = feeRate == null ? [] : feeRate.split("#");
|
|
117
|
+
if (hashArr.length <= 1)
|
|
118
|
+
return null;
|
|
119
|
+
const arr = hashArr[1].split(";");
|
|
120
|
+
if (arr.length <= 1)
|
|
121
|
+
return null;
|
|
122
|
+
return {
|
|
123
|
+
balance: new BN(arr[1]),
|
|
124
|
+
initAta: arr[0] === "1"
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Checks whether a wrap instruction (SOL -> WSOL) should be a part of the signed init message
|
|
129
|
+
*
|
|
130
|
+
* @param swapData
|
|
131
|
+
* @param feeRate
|
|
132
|
+
* @private
|
|
133
|
+
* @returns {boolean} returns true if wrap instruction should be added
|
|
134
|
+
*/
|
|
135
|
+
shouldWrapOnInit(swapData, feeRate) {
|
|
136
|
+
const data = this.extractAtaDataFromFeeRate(feeRate);
|
|
137
|
+
if (data == null)
|
|
138
|
+
return false;
|
|
139
|
+
return data.balance.lt(swapData.amount);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Returns the transaction to be signed as an initialization signature from the intermediary, also adds
|
|
143
|
+
* SOL to WSOL wrapping if indicated by the fee rate
|
|
144
|
+
*
|
|
145
|
+
* @param swapData
|
|
146
|
+
* @param timeout
|
|
147
|
+
* @param feeRate
|
|
148
|
+
* @private
|
|
149
|
+
*/
|
|
150
|
+
getTxToSign(swapData, timeout, feeRate) {
|
|
151
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
152
|
+
const action = swapData.isPayIn() ?
|
|
153
|
+
yield this.InitPayIn(swapData, new BN(timeout), feeRate) :
|
|
154
|
+
yield this.InitNotPayIn(swapData, new BN(timeout));
|
|
155
|
+
const tx = (yield action.tx(feeRate)).tx;
|
|
156
|
+
return tx;
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Returns auth prefix to be used with a specific swap, payIn=true & payIn=false use different prefixes (these
|
|
161
|
+
* actually have no meaning for the smart contract/solana program in the Solana case)
|
|
162
|
+
*
|
|
163
|
+
* @param swapData
|
|
164
|
+
* @private
|
|
165
|
+
*/
|
|
166
|
+
getAuthPrefix(swapData) {
|
|
167
|
+
return swapData.isPayIn() ? "claim_initialize" : "initialize";
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Returns "processed" slot required for signature validation, uses preFetchedData if provided & valid
|
|
171
|
+
*
|
|
172
|
+
* @param preFetchedData
|
|
173
|
+
* @private
|
|
174
|
+
*/
|
|
175
|
+
getSlotForSignature(preFetchedData) {
|
|
176
|
+
if (preFetchedData != null &&
|
|
177
|
+
preFetchedData.latestSlot != null &&
|
|
178
|
+
preFetchedData.latestSlot.timestamp > Date.now() - this.root.Slots.SLOT_CACHE_TIME) {
|
|
179
|
+
const estimatedSlotsPassed = Math.floor((Date.now() - preFetchedData.latestSlot.timestamp) / this.root.SLOT_TIME);
|
|
180
|
+
const estimatedCurrentSlot = preFetchedData.latestSlot.slot + estimatedSlotsPassed;
|
|
181
|
+
this.logger.debug("getSlotForSignature(): slot: " + preFetchedData.latestSlot.slot +
|
|
182
|
+
" estimated passed slots: " + estimatedSlotsPassed + " estimated current slot: " + estimatedCurrentSlot);
|
|
183
|
+
return Promise.resolve(estimatedCurrentSlot);
|
|
184
|
+
}
|
|
185
|
+
return this.root.Slots.getSlot("processed");
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Returns blockhash required for signature validation, uses preFetchedData if provided & valid
|
|
189
|
+
*
|
|
190
|
+
* @param txSlot
|
|
191
|
+
* @param preFetchedData
|
|
192
|
+
* @private
|
|
193
|
+
*/
|
|
194
|
+
getBlockhashForSignature(txSlot, preFetchedData) {
|
|
195
|
+
if (preFetchedData != null &&
|
|
196
|
+
preFetchedData.transactionSlot != null &&
|
|
197
|
+
preFetchedData.transactionSlot.slot === txSlot) {
|
|
198
|
+
return Promise.resolve(preFetchedData.transactionSlot.blockhash);
|
|
199
|
+
}
|
|
200
|
+
return this.root.Blocks.getParsedBlock(txSlot).then(val => val.blockhash);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Pre-fetches slot & block based on priorly received SolanaPreFetchData, such that it can later be used
|
|
204
|
+
* by signature verification
|
|
205
|
+
*
|
|
206
|
+
* @param data
|
|
207
|
+
*/
|
|
208
|
+
preFetchForInitSignatureVerification(data) {
|
|
209
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
210
|
+
const [latestSlot, txBlock] = yield Promise.all([
|
|
211
|
+
this.root.Slots.getSlotAndTimestamp("processed"),
|
|
212
|
+
this.root.Blocks.getParsedBlock(data.slot)
|
|
213
|
+
]);
|
|
214
|
+
return {
|
|
215
|
+
latestSlot,
|
|
216
|
+
transactionSlot: {
|
|
217
|
+
slot: data.slot,
|
|
218
|
+
blockhash: txBlock.blockhash
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Pre-fetches block data required for signing the init message by the LP, this can happen in parallel before
|
|
225
|
+
* signing takes place making the quoting quicker
|
|
226
|
+
*/
|
|
227
|
+
preFetchBlockDataForSignatures() {
|
|
228
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
229
|
+
const latestParsedBlock = yield this.root.Blocks.findLatestParsedBlock("finalized");
|
|
230
|
+
return {
|
|
231
|
+
block: latestParsedBlock.block,
|
|
232
|
+
slot: latestParsedBlock.slot,
|
|
233
|
+
timestamp: Date.now()
|
|
234
|
+
};
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Signs swap initialization authorization, using data from preFetchedBlockData if provided & still valid (subject
|
|
239
|
+
* to SIGNATURE_PREFETCH_DATA_VALIDITY)
|
|
240
|
+
*
|
|
241
|
+
* @param signer
|
|
242
|
+
* @param swapData
|
|
243
|
+
* @param authorizationTimeout
|
|
244
|
+
* @param feeRate
|
|
245
|
+
* @param preFetchedBlockData
|
|
246
|
+
* @public
|
|
247
|
+
*/
|
|
248
|
+
signSwapInitialization(signer, swapData, authorizationTimeout, preFetchedBlockData, feeRate) {
|
|
249
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
250
|
+
if (signer.keypair == null)
|
|
251
|
+
throw new Error("Unsupported");
|
|
252
|
+
if (!signer.getPublicKey().equals(swapData.isPayIn() ? swapData.claimer : swapData.offerer))
|
|
253
|
+
throw new Error("Invalid signer, wrong public key!");
|
|
254
|
+
if (preFetchedBlockData != null && Date.now() - preFetchedBlockData.timestamp > this.SIGNATURE_PREFETCH_DATA_VALIDITY)
|
|
255
|
+
preFetchedBlockData = null;
|
|
256
|
+
const { block: latestBlock, slot: latestSlot } = preFetchedBlockData || (yield this.root.Blocks.findLatestParsedBlock("finalized"));
|
|
257
|
+
const authTimeout = Math.floor(Date.now() / 1000) + authorizationTimeout;
|
|
258
|
+
const txToSign = yield this.getTxToSign(swapData, authTimeout.toString(10), feeRate);
|
|
259
|
+
txToSign.feePayer = swapData.isPayIn() ? swapData.offerer : swapData.claimer;
|
|
260
|
+
txToSign.recentBlockhash = latestBlock.blockhash;
|
|
261
|
+
txToSign.sign(signer.keypair);
|
|
262
|
+
this.logger.debug("signSwapInitialization(): Signed tx: ", txToSign);
|
|
263
|
+
const sig = txToSign.signatures.find(e => e.publicKey.equals(signer.getPublicKey()));
|
|
264
|
+
return {
|
|
265
|
+
prefix: this.getAuthPrefix(swapData),
|
|
266
|
+
timeout: authTimeout.toString(10),
|
|
267
|
+
signature: latestSlot + ";" + sig.signature.toString("hex")
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Checks whether the provided signature data is valid, using preFetchedData if provided and still valid
|
|
273
|
+
*
|
|
274
|
+
* @param swapData
|
|
275
|
+
* @param timeout
|
|
276
|
+
* @param prefix
|
|
277
|
+
* @param signature
|
|
278
|
+
* @param feeRate
|
|
279
|
+
* @param preFetchedData
|
|
280
|
+
* @public
|
|
281
|
+
*/
|
|
282
|
+
isSignatureValid(swapData, timeout, prefix, signature, feeRate, preFetchedData) {
|
|
283
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
284
|
+
const sender = swapData.isPayIn() ? swapData.offerer : swapData.claimer;
|
|
285
|
+
const signer = swapData.isPayIn() ? swapData.claimer : swapData.offerer;
|
|
286
|
+
if (!swapData.isPayIn() && this.root.isExpired(sender.toString(), swapData)) {
|
|
287
|
+
throw new base_1.SignatureVerificationError("Swap will expire too soon!");
|
|
288
|
+
}
|
|
289
|
+
if (prefix !== this.getAuthPrefix(swapData))
|
|
290
|
+
throw new base_1.SignatureVerificationError("Invalid prefix");
|
|
291
|
+
const currentTimestamp = new BN(Math.floor(Date.now() / 1000));
|
|
292
|
+
const isExpired = new BN(timeout).sub(currentTimestamp).lt(new BN(this.root.authGracePeriod));
|
|
293
|
+
if (isExpired)
|
|
294
|
+
throw new base_1.SignatureVerificationError("Authorization expired!");
|
|
295
|
+
const [transactionSlot, signatureString] = signature.split(";");
|
|
296
|
+
const txSlot = parseInt(transactionSlot);
|
|
297
|
+
const [latestSlot, blockhash] = yield Promise.all([
|
|
298
|
+
this.getSlotForSignature(preFetchedData),
|
|
299
|
+
this.getBlockhashForSignature(txSlot, preFetchedData)
|
|
300
|
+
]);
|
|
301
|
+
const lastValidTransactionSlot = txSlot + this.root.TX_SLOT_VALIDITY;
|
|
302
|
+
const slotsLeft = lastValidTransactionSlot - latestSlot - this.SIGNATURE_SLOT_BUFFER;
|
|
303
|
+
if (slotsLeft < 0)
|
|
304
|
+
throw new base_1.SignatureVerificationError("Authorization expired!");
|
|
305
|
+
const txToSign = yield this.getTxToSign(swapData, timeout, feeRate);
|
|
306
|
+
txToSign.feePayer = sender;
|
|
307
|
+
txToSign.recentBlockhash = blockhash;
|
|
308
|
+
txToSign.addSignature(signer, buffer_1.Buffer.from(signatureString, "hex"));
|
|
309
|
+
this.logger.debug("isSignatureValid(): Signed tx: ", txToSign);
|
|
310
|
+
const valid = txToSign.verifySignatures(false);
|
|
311
|
+
if (!valid)
|
|
312
|
+
throw new base_1.SignatureVerificationError("Invalid signature!");
|
|
313
|
+
return buffer_1.Buffer.from(blockhash);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Gets expiry of the provided signature data, this is a minimum of slot expiry & swap signature expiry
|
|
318
|
+
*
|
|
319
|
+
* @param timeout
|
|
320
|
+
* @param signature
|
|
321
|
+
* @param preFetchedData
|
|
322
|
+
* @public
|
|
323
|
+
*/
|
|
324
|
+
getSignatureExpiry(timeout, signature, preFetchedData) {
|
|
325
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
326
|
+
const [transactionSlotStr, signatureString] = signature.split(";");
|
|
327
|
+
const txSlot = parseInt(transactionSlotStr);
|
|
328
|
+
const latestSlot = yield this.getSlotForSignature(preFetchedData);
|
|
329
|
+
const lastValidTransactionSlot = txSlot + this.root.TX_SLOT_VALIDITY;
|
|
330
|
+
const slotsLeft = lastValidTransactionSlot - latestSlot - this.SIGNATURE_SLOT_BUFFER;
|
|
331
|
+
const now = Date.now();
|
|
332
|
+
const slotExpiryTime = now + (slotsLeft * this.root.SLOT_TIME);
|
|
333
|
+
const timeoutExpiryTime = (parseInt(timeout) - this.root.authGracePeriod) * 1000;
|
|
334
|
+
const expiry = Math.min(slotExpiryTime, timeoutExpiryTime);
|
|
335
|
+
if (expiry < now)
|
|
336
|
+
return 0;
|
|
337
|
+
return expiry;
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Checks whether signature is expired for good (uses "finalized" slot)
|
|
342
|
+
*
|
|
343
|
+
* @param signature
|
|
344
|
+
* @param timeout
|
|
345
|
+
* @public
|
|
346
|
+
*/
|
|
347
|
+
isSignatureExpired(signature, timeout) {
|
|
348
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
349
|
+
const [transactionSlotStr, signatureString] = signature.split(";");
|
|
350
|
+
const txSlot = parseInt(transactionSlotStr);
|
|
351
|
+
const lastValidTransactionSlot = txSlot + this.root.TX_SLOT_VALIDITY;
|
|
352
|
+
const latestSlot = yield this.root.Slots.getSlot("finalized");
|
|
353
|
+
const slotsLeft = lastValidTransactionSlot - latestSlot + this.SIGNATURE_SLOT_BUFFER;
|
|
354
|
+
if (slotsLeft < 0)
|
|
355
|
+
return true;
|
|
356
|
+
if ((parseInt(timeout) + this.root.authGracePeriod) * 1000 < Date.now())
|
|
357
|
+
return true;
|
|
358
|
+
return false;
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Creates init transaction (InitPayIn) with a valid signature from an LP, also adds a SOL to WSOL wrapping ix to
|
|
363
|
+
* the init transaction (if indicated by the fee rate) or adds the wrapping in a separate transaction (if no
|
|
364
|
+
* indication in the fee rate)
|
|
365
|
+
*
|
|
366
|
+
* @param swapData swap to initialize
|
|
367
|
+
* @param timeout init signature timeout
|
|
368
|
+
* @param prefix init signature prefix
|
|
369
|
+
* @param signature init signature
|
|
370
|
+
* @param skipChecks whether to skip signature validity checks
|
|
371
|
+
* @param feeRate fee rate to use for the transaction
|
|
372
|
+
*/
|
|
373
|
+
txsInitPayIn(swapData, timeout, prefix, signature, skipChecks, feeRate) {
|
|
374
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
375
|
+
if (!skipChecks) {
|
|
376
|
+
const [_, payStatus] = yield Promise.all([
|
|
377
|
+
(0, Utils_1.tryWithRetries)(() => this.isSignatureValid(swapData, timeout, prefix, signature, feeRate), this.retryPolicy, (e) => e instanceof base_1.SignatureVerificationError),
|
|
378
|
+
(0, Utils_1.tryWithRetries)(() => this.root.getPaymentHashStatus(swapData.paymentHash), this.retryPolicy)
|
|
379
|
+
]);
|
|
380
|
+
if (payStatus !== base_1.SwapCommitStatus.NOT_COMMITED)
|
|
381
|
+
throw new base_1.SwapDataVerificationError("Invoice already being paid for or paid");
|
|
382
|
+
}
|
|
383
|
+
const [slotNumber, signatureStr] = signature.split(";");
|
|
384
|
+
const block = yield (0, Utils_1.tryWithRetries)(() => this.root.Blocks.getParsedBlock(parseInt(slotNumber)), this.retryPolicy);
|
|
385
|
+
const txs = [];
|
|
386
|
+
let isWrapping = false;
|
|
387
|
+
const isWrappedInSignedTx = feeRate != null && feeRate.split("#").length > 1;
|
|
388
|
+
if (!isWrappedInSignedTx && swapData.token.equals(this.root.Tokens.WSOL_ADDRESS)) {
|
|
389
|
+
const ataAcc = yield (0, Utils_1.tryWithRetries)(() => this.root.Tokens.getATAOrNull(swapData.offererAta), this.retryPolicy);
|
|
390
|
+
const balance = ataAcc == null ? new BN(0) : new BN(ataAcc.amount.toString());
|
|
391
|
+
if (balance.lt(swapData.amount)) {
|
|
392
|
+
//Need to wrap more SOL to WSOL
|
|
393
|
+
yield this.root.Tokens.Wrap(swapData.offerer, swapData.amount.sub(balance), ataAcc == null)
|
|
394
|
+
.addToTxs(txs, feeRate, block);
|
|
395
|
+
isWrapping = true;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
const initTx = yield (yield this.InitPayIn(swapData, new BN(timeout), feeRate)).tx(feeRate, block);
|
|
399
|
+
initTx.tx.addSignature(swapData.claimer, buffer_1.Buffer.from(signatureStr, "hex"));
|
|
400
|
+
txs.push(initTx);
|
|
401
|
+
this.logger.debug("txsInitPayIn(): create swap init TX, swap: " + swapData.getHash() +
|
|
402
|
+
" wrapping client-side: " + isWrapping + " feerate: " + feeRate);
|
|
403
|
+
return txs;
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Creates init transactions (InitNotPayIn) with a valid signature from an intermediary
|
|
408
|
+
*
|
|
409
|
+
* @param swapData swap to initialize
|
|
410
|
+
* @param timeout init signature timeout
|
|
411
|
+
* @param prefix init signature prefix
|
|
412
|
+
* @param signature init signature
|
|
413
|
+
* @param skipChecks whether to skip signature validity checks
|
|
414
|
+
* @param feeRate fee rate to use for the transaction
|
|
415
|
+
*/
|
|
416
|
+
txsInit(swapData, timeout, prefix, signature, skipChecks, feeRate) {
|
|
417
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
418
|
+
if (!skipChecks) {
|
|
419
|
+
yield (0, Utils_1.tryWithRetries)(() => this.isSignatureValid(swapData, timeout, prefix, signature, feeRate), this.retryPolicy, (e) => e instanceof base_1.SignatureVerificationError);
|
|
420
|
+
}
|
|
421
|
+
const [slotNumber, signatureStr] = signature.split(";");
|
|
422
|
+
const block = yield (0, Utils_1.tryWithRetries)(() => this.root.Blocks.getParsedBlock(parseInt(slotNumber)), this.retryPolicy);
|
|
423
|
+
const initTx = yield (yield this.InitNotPayIn(swapData, new BN(timeout))).tx(feeRate, block);
|
|
424
|
+
initTx.tx.addSignature(swapData.offerer, buffer_1.Buffer.from(signatureStr, "hex"));
|
|
425
|
+
this.logger.debug("txsInit(): create swap init TX, swap: " + swapData.getHash() + " feerate: " + feeRate);
|
|
426
|
+
return [initTx];
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Returns the fee rate to be used for a specific init transaction, also adding indication whether the WSOL ATA
|
|
431
|
+
* should be initialized in the init transaction and/or current balance in the WSOL ATA
|
|
432
|
+
*
|
|
433
|
+
* @param offerer
|
|
434
|
+
* @param claimer
|
|
435
|
+
* @param token
|
|
436
|
+
* @param paymentHash
|
|
437
|
+
*/
|
|
438
|
+
getInitPayInFeeRate(offerer, claimer, token, paymentHash) {
|
|
439
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
440
|
+
const accounts = [];
|
|
441
|
+
if (offerer != null)
|
|
442
|
+
accounts.push(offerer);
|
|
443
|
+
if (token != null) {
|
|
444
|
+
accounts.push(this.root.SwapVault(token));
|
|
445
|
+
if (offerer != null)
|
|
446
|
+
accounts.push((0, spl_token_1.getAssociatedTokenAddressSync)(token, offerer));
|
|
447
|
+
if (claimer != null)
|
|
448
|
+
accounts.push(this.root.SwapUserVault(claimer, token));
|
|
449
|
+
}
|
|
450
|
+
if (paymentHash != null)
|
|
451
|
+
accounts.push(this.root.SwapEscrowState(buffer_1.Buffer.from(paymentHash, "hex")));
|
|
452
|
+
const shouldCheckWSOLAta = token != null && offerer != null && token.equals(this.root.Tokens.WSOL_ADDRESS);
|
|
453
|
+
let [feeRate, _account] = yield Promise.all([
|
|
454
|
+
this.root.Fees.getFeeRate(accounts),
|
|
455
|
+
shouldCheckWSOLAta ?
|
|
456
|
+
this.root.Tokens.getATAOrNull((0, spl_token_1.getAssociatedTokenAddressSync)(token, offerer)) :
|
|
457
|
+
Promise.resolve(null)
|
|
458
|
+
]);
|
|
459
|
+
if (shouldCheckWSOLAta) {
|
|
460
|
+
const account = _account;
|
|
461
|
+
const balance = account == null ? new BN(0) : new BN(account.amount.toString());
|
|
462
|
+
//Add an indication about whether the ATA is initialized & balance it contains
|
|
463
|
+
feeRate += "#" + (account != null ? "0" : "1") + ";" + balance.toString(10);
|
|
464
|
+
}
|
|
465
|
+
this.logger.debug("getInitPayInFeeRate(): feerate computed: " + feeRate);
|
|
466
|
+
return feeRate;
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Returns the fee rate to be used for a specific init transaction
|
|
471
|
+
*
|
|
472
|
+
* @param offerer
|
|
473
|
+
* @param claimer
|
|
474
|
+
* @param token
|
|
475
|
+
* @param paymentHash
|
|
476
|
+
*/
|
|
477
|
+
getInitFeeRate(offerer, claimer, token, paymentHash) {
|
|
478
|
+
const accounts = [];
|
|
479
|
+
if (offerer != null && token != null)
|
|
480
|
+
accounts.push(this.root.SwapUserVault(offerer, token));
|
|
481
|
+
if (claimer != null)
|
|
482
|
+
accounts.push(claimer);
|
|
483
|
+
if (paymentHash != null)
|
|
484
|
+
accounts.push(this.root.SwapEscrowState(buffer_1.Buffer.from(paymentHash, "hex")));
|
|
485
|
+
return this.root.Fees.getFeeRate(accounts);
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Get the estimated solana fee of the init transaction, this includes the required deposit for creating swap PDA
|
|
489
|
+
*/
|
|
490
|
+
getInitFee(swapData, feeRate) {
|
|
491
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
492
|
+
if (swapData == null)
|
|
493
|
+
return new BN(this.root.ESCROW_STATE_RENT_EXEMPT + 10000);
|
|
494
|
+
feeRate = feeRate ||
|
|
495
|
+
(swapData.payIn
|
|
496
|
+
? yield this.getInitPayInFeeRate(swapData.offerer, swapData.claimer, swapData.token, swapData.paymentHash)
|
|
497
|
+
: yield this.getInitFeeRate(swapData.offerer, swapData.claimer, swapData.token, swapData.paymentHash));
|
|
498
|
+
const computeBudget = swapData.payIn ? SwapInit.CUCosts.INIT_PAY_IN : SwapInit.CUCosts.INIT;
|
|
499
|
+
const baseFee = swapData.payIn ? 10000 : 10000 + 5000;
|
|
500
|
+
return new BN(this.root.ESCROW_STATE_RENT_EXEMPT + baseFee).add(this.root.Fees.getPriorityFee(computeBudget, feeRate));
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
exports.SwapInit = SwapInit;
|
|
505
|
+
SwapInit.CUCosts = {
|
|
506
|
+
INIT: 90000,
|
|
507
|
+
INIT_PAY_IN: 50000,
|
|
508
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { SolanaSwapModule } from "../SolanaSwapModule";
|
|
3
|
+
import { SolanaSwapData } from "../SolanaSwapData";
|
|
4
|
+
import * as BN from "bn.js";
|
|
5
|
+
import { SolanaTx } from "../../base/modules/SolanaTransactions";
|
|
6
|
+
import { Buffer } from "buffer";
|
|
7
|
+
import { SolanaSigner } from "../../wallet/SolanaSigner";
|
|
8
|
+
export declare class SwapRefund extends SolanaSwapModule {
|
|
9
|
+
private static readonly CUCosts;
|
|
10
|
+
/**
|
|
11
|
+
* Action for generic Refund instruction
|
|
12
|
+
*
|
|
13
|
+
* @param swapData
|
|
14
|
+
* @param refundAuthTimeout optional refund authorization timeout (should be 0 for refunding expired swaps)
|
|
15
|
+
* @constructor
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
private Refund;
|
|
19
|
+
/**
|
|
20
|
+
* Action for refunding with signature, adds the Ed25519 verify instruction
|
|
21
|
+
*
|
|
22
|
+
* @param swapData
|
|
23
|
+
* @param timeout
|
|
24
|
+
* @param prefix
|
|
25
|
+
* @param signature
|
|
26
|
+
* @constructor
|
|
27
|
+
* @private
|
|
28
|
+
*/
|
|
29
|
+
private RefundWithSignature;
|
|
30
|
+
/**
|
|
31
|
+
* Gets the message to be signed as a refund authorization
|
|
32
|
+
*
|
|
33
|
+
* @param swapData
|
|
34
|
+
* @param prefix
|
|
35
|
+
* @param timeout
|
|
36
|
+
* @private
|
|
37
|
+
*/
|
|
38
|
+
private getRefundMessage;
|
|
39
|
+
/**
|
|
40
|
+
* Checks whether we should unwrap the WSOL to SOL when refunding the swap
|
|
41
|
+
*
|
|
42
|
+
* @param swapData
|
|
43
|
+
* @private
|
|
44
|
+
*/
|
|
45
|
+
private shouldUnwrap;
|
|
46
|
+
signSwapRefund(signer: SolanaSigner, swapData: SolanaSwapData, authorizationTimeout: number): Promise<{
|
|
47
|
+
prefix: string;
|
|
48
|
+
timeout: string;
|
|
49
|
+
signature: string;
|
|
50
|
+
}>;
|
|
51
|
+
isSignatureValid(swapData: SolanaSwapData, timeout: string, prefix: string, signature: string): Promise<Buffer>;
|
|
52
|
+
/**
|
|
53
|
+
* Creates transactions required for refunding timed out swap, also unwraps WSOL to SOL
|
|
54
|
+
*
|
|
55
|
+
* @param swapData swap data to refund
|
|
56
|
+
* @param check whether to check if swap is already expired and refundable
|
|
57
|
+
* @param initAta should initialize ATA if it doesn't exist
|
|
58
|
+
* @param feeRate fee rate to be used for the transactions
|
|
59
|
+
*/
|
|
60
|
+
txsRefund(swapData: SolanaSwapData, check?: boolean, initAta?: boolean, feeRate?: string): Promise<SolanaTx[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Creates transactions required for refunding the swap with authorization signature, also unwraps WSOL to SOL
|
|
63
|
+
*
|
|
64
|
+
* @param swapData swap data to refund
|
|
65
|
+
* @param timeout signature timeout
|
|
66
|
+
* @param prefix signature prefix of the counterparty
|
|
67
|
+
* @param signature signature of the counterparty
|
|
68
|
+
* @param check whether to check if swap is committed before attempting refund
|
|
69
|
+
* @param initAta should initialize ATA if it doesn't exist
|
|
70
|
+
* @param feeRate fee rate to be used for the transactions
|
|
71
|
+
*/
|
|
72
|
+
txsRefundWithAuthorization(swapData: SolanaSwapData, timeout: string, prefix: string, signature: string, check?: boolean, initAta?: boolean, feeRate?: string): Promise<SolanaTx[]>;
|
|
73
|
+
getRefundFeeRate(swapData: SolanaSwapData): Promise<string>;
|
|
74
|
+
/**
|
|
75
|
+
* Get the estimated solana transaction fee of the refund transaction, this fee might be negative since it
|
|
76
|
+
* includes the rebate for closing the swap PDA
|
|
77
|
+
*/
|
|
78
|
+
getRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<BN>;
|
|
79
|
+
/**
|
|
80
|
+
* Get the estimated solana transaction fee of the refund transaction
|
|
81
|
+
*/
|
|
82
|
+
getRawRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<BN>;
|
|
83
|
+
}
|