@atomiqlabs/chain-solana 13.5.13 → 13.5.14
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 -201
- package/README.md +73 -73
- package/dist/index.d.ts +81 -81
- package/dist/index.js +102 -102
- package/dist/node/index.d.ts +9 -9
- package/dist/node/index.js +13 -13
- package/dist/solana/SolanaChainType.d.ts +15 -15
- package/dist/solana/SolanaChainType.js +2 -2
- package/dist/solana/SolanaChains.d.ts +12 -12
- package/dist/solana/SolanaChains.js +45 -45
- package/dist/solana/SolanaInitializer.d.ts +94 -94
- package/dist/solana/SolanaInitializer.js +174 -174
- package/dist/solana/btcrelay/SolanaBtcRelay.d.ts +222 -222
- package/dist/solana/btcrelay/SolanaBtcRelay.js +455 -455
- package/dist/solana/btcrelay/headers/SolanaBtcHeader.d.ts +84 -84
- package/dist/solana/btcrelay/headers/SolanaBtcHeader.js +70 -70
- package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.d.ts +92 -92
- package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.js +109 -109
- package/dist/solana/btcrelay/program/programIdl.json +671 -671
- package/dist/solana/chain/SolanaAction.d.ts +26 -26
- package/dist/solana/chain/SolanaAction.js +87 -87
- package/dist/solana/chain/SolanaChainInterface.d.ts +224 -224
- package/dist/solana/chain/SolanaChainInterface.js +275 -275
- package/dist/solana/chain/SolanaModule.d.ts +14 -14
- package/dist/solana/chain/SolanaModule.js +13 -13
- package/dist/solana/chain/modules/SolanaAddresses.d.ts +8 -8
- package/dist/solana/chain/modules/SolanaAddresses.js +22 -22
- package/dist/solana/chain/modules/SolanaBlocks.d.ts +32 -32
- package/dist/solana/chain/modules/SolanaBlocks.js +78 -78
- package/dist/solana/chain/modules/SolanaEvents.d.ts +68 -68
- package/dist/solana/chain/modules/SolanaEvents.js +238 -238
- package/dist/solana/chain/modules/SolanaFees.d.ts +189 -189
- package/dist/solana/chain/modules/SolanaFees.js +434 -434
- package/dist/solana/chain/modules/SolanaSignatures.d.ts +24 -24
- package/dist/solana/chain/modules/SolanaSignatures.js +39 -39
- package/dist/solana/chain/modules/SolanaSlots.d.ts +33 -33
- package/dist/solana/chain/modules/SolanaSlots.js +72 -72
- package/dist/solana/chain/modules/SolanaTokens.d.ts +123 -123
- package/dist/solana/chain/modules/SolanaTokens.js +242 -242
- package/dist/solana/chain/modules/SolanaTransactions.d.ts +149 -149
- package/dist/solana/chain/modules/SolanaTransactions.js +445 -445
- package/dist/solana/connection/ConnectionWithRetries.d.ts +35 -35
- package/dist/solana/connection/ConnectionWithRetries.js +86 -71
- package/dist/solana/events/SolanaChainEvents.d.ts +45 -45
- package/dist/solana/events/SolanaChainEvents.js +108 -108
- package/dist/solana/events/SolanaChainEventsBrowser.d.ts +205 -205
- package/dist/solana/events/SolanaChainEventsBrowser.js +404 -404
- package/dist/solana/program/SolanaProgramBase.d.ts +73 -73
- package/dist/solana/program/SolanaProgramBase.js +54 -54
- package/dist/solana/program/SolanaProgramModule.d.ts +8 -8
- package/dist/solana/program/SolanaProgramModule.js +11 -11
- package/dist/solana/program/modules/SolanaProgramEvents.d.ts +53 -53
- package/dist/solana/program/modules/SolanaProgramEvents.js +117 -117
- package/dist/solana/swaps/SolanaSwapData.d.ts +333 -333
- package/dist/solana/swaps/SolanaSwapData.js +535 -535
- package/dist/solana/swaps/SolanaSwapModule.d.ts +11 -11
- package/dist/solana/swaps/SolanaSwapModule.js +12 -12
- package/dist/solana/swaps/SolanaSwapProgram.d.ts +376 -376
- package/dist/solana/swaps/SolanaSwapProgram.js +769 -769
- package/dist/solana/swaps/SwapTypeEnum.d.ts +11 -11
- package/dist/solana/swaps/SwapTypeEnum.js +43 -43
- package/dist/solana/swaps/modules/SolanaDataAccount.d.ts +95 -95
- package/dist/solana/swaps/modules/SolanaDataAccount.js +232 -232
- package/dist/solana/swaps/modules/SolanaLpVault.d.ts +69 -69
- package/dist/solana/swaps/modules/SolanaLpVault.js +171 -171
- package/dist/solana/swaps/modules/SwapClaim.d.ts +126 -126
- package/dist/solana/swaps/modules/SwapClaim.js +294 -294
- package/dist/solana/swaps/modules/SwapInit.d.ts +213 -213
- package/dist/solana/swaps/modules/SwapInit.js +658 -658
- package/dist/solana/swaps/modules/SwapRefund.d.ts +87 -87
- package/dist/solana/swaps/modules/SwapRefund.js +293 -293
- package/dist/solana/swaps/programIdl.json +945 -945
- package/dist/solana/swaps/programTypes.d.ts +943 -943
- package/dist/solana/swaps/programTypes.js +945 -945
- package/dist/solana/swaps/v1/programIdl.json +945 -945
- package/dist/solana/swaps/v1/programTypes.d.ts +943 -943
- package/dist/solana/swaps/v1/programTypes.js +945 -945
- package/dist/solana/swaps/v2/programIdl.json +952 -952
- package/dist/solana/swaps/v2/programTypes.d.ts +950 -950
- package/dist/solana/swaps/v2/programTypes.js +952 -952
- package/dist/solana/wallet/SolanaKeypairWallet.d.ts +29 -29
- package/dist/solana/wallet/SolanaKeypairWallet.js +50 -50
- package/dist/solana/wallet/SolanaSigner.d.ts +30 -30
- package/dist/solana/wallet/SolanaSigner.js +30 -30
- package/dist/utils/Utils.d.ts +58 -58
- package/dist/utils/Utils.js +170 -170
- package/node/index.d.ts +1 -1
- package/node/index.js +3 -3
- package/package.json +46 -46
- package/src/index.ts +87 -87
- package/src/node/index.ts +9 -9
- package/src/solana/SolanaChainType.ts +32 -32
- package/src/solana/SolanaChains.ts +46 -46
- package/src/solana/SolanaInitializer.ts +278 -278
- package/src/solana/btcrelay/SolanaBtcRelay.ts +615 -615
- package/src/solana/btcrelay/headers/SolanaBtcHeader.ts +116 -116
- package/src/solana/btcrelay/headers/SolanaBtcStoredHeader.ts +148 -148
- package/src/solana/btcrelay/program/programIdl.json +670 -670
- package/src/solana/chain/SolanaAction.ts +109 -109
- package/src/solana/chain/SolanaChainInterface.ts +404 -404
- package/src/solana/chain/SolanaModule.ts +20 -20
- package/src/solana/chain/modules/SolanaAddresses.ts +20 -20
- package/src/solana/chain/modules/SolanaBlocks.ts +89 -89
- package/src/solana/chain/modules/SolanaEvents.ts +271 -271
- package/src/solana/chain/modules/SolanaFees.ts +522 -522
- package/src/solana/chain/modules/SolanaSignatures.ts +39 -39
- package/src/solana/chain/modules/SolanaSlots.ts +85 -85
- package/src/solana/chain/modules/SolanaTokens.ts +300 -300
- package/src/solana/chain/modules/SolanaTransactions.ts +503 -503
- package/src/solana/connection/ConnectionWithRetries.ts +113 -96
- package/src/solana/events/SolanaChainEvents.ts +127 -127
- package/src/solana/events/SolanaChainEventsBrowser.ts +495 -495
- package/src/solana/program/SolanaProgramBase.ts +119 -119
- package/src/solana/program/SolanaProgramModule.ts +15 -15
- package/src/solana/program/modules/SolanaProgramEvents.ts +157 -157
- package/src/solana/swaps/SolanaSwapData.ts +735 -735
- package/src/solana/swaps/SolanaSwapModule.ts +19 -19
- package/src/solana/swaps/SolanaSwapProgram.ts +1074 -1074
- package/src/solana/swaps/SwapTypeEnum.ts +30 -30
- package/src/solana/swaps/modules/SolanaDataAccount.ts +302 -302
- package/src/solana/swaps/modules/SolanaLpVault.ts +208 -208
- package/src/solana/swaps/modules/SwapClaim.ts +387 -387
- package/src/solana/swaps/modules/SwapInit.ts +785 -785
- package/src/solana/swaps/modules/SwapRefund.ts +353 -353
- package/src/solana/swaps/v1/programIdl.json +944 -944
- package/src/solana/swaps/v1/programTypes.ts +1885 -1885
- package/src/solana/swaps/v2/programIdl.json +951 -951
- package/src/solana/swaps/v2/programTypes.ts +1899 -1899
- package/src/solana/wallet/SolanaKeypairWallet.ts +56 -56
- package/src/solana/wallet/SolanaSigner.ts +43 -43
- package/src/utils/Utils.ts +194 -194
|
@@ -1,293 +1,293 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SwapRefund = void 0;
|
|
4
|
-
const SolanaSwapModule_1 = require("../SolanaSwapModule");
|
|
5
|
-
const sha2_1 = require("@noble/hashes/sha2");
|
|
6
|
-
const tweetnacl_1 = require("tweetnacl");
|
|
7
|
-
const base_1 = require("@atomiqlabs/base");
|
|
8
|
-
const web3_js_1 = require("@solana/web3.js");
|
|
9
|
-
const spl_token_1 = require("@solana/spl-token");
|
|
10
|
-
const SolanaAction_1 = require("../../chain/SolanaAction");
|
|
11
|
-
const Utils_1 = require("../../../utils/Utils");
|
|
12
|
-
const buffer_1 = require("buffer");
|
|
13
|
-
const SolanaTokens_1 = require("../../chain/modules/SolanaTokens");
|
|
14
|
-
const BN = require("bn.js");
|
|
15
|
-
const SolanaSwapProgram_1 = require("../SolanaSwapProgram");
|
|
16
|
-
class SwapRefund extends SolanaSwapModule_1.SolanaSwapModule {
|
|
17
|
-
/**
|
|
18
|
-
* Action for generic Refund instruction
|
|
19
|
-
*
|
|
20
|
-
* @param signer
|
|
21
|
-
* @param swapData
|
|
22
|
-
* @param refundAuthTimeout optional refund authorization timeout (should be 0 for refunding expired swaps)
|
|
23
|
-
* @private
|
|
24
|
-
*/
|
|
25
|
-
async Refund(signer, swapData, refundAuthTimeout) {
|
|
26
|
-
const accounts = {
|
|
27
|
-
offerer: swapData.offerer,
|
|
28
|
-
claimer: swapData.claimer,
|
|
29
|
-
escrowState: this.program._SwapEscrowState(buffer_1.Buffer.from(swapData.paymentHash, "hex")),
|
|
30
|
-
claimerUserData: !swapData.payOut ? this.program._SwapUserVault(swapData.claimer, swapData.token) : null,
|
|
31
|
-
ixSysvar: refundAuthTimeout != null ? web3_js_1.SYSVAR_INSTRUCTIONS_PUBKEY : null
|
|
32
|
-
};
|
|
33
|
-
const useTimeout = refundAuthTimeout != null ? refundAuthTimeout : 0n;
|
|
34
|
-
if (swapData.isPayIn()) {
|
|
35
|
-
const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(swapData.token, swapData.offerer);
|
|
36
|
-
return new SolanaAction_1.SolanaAction(signer, this.root, await this.swapProgram.methods
|
|
37
|
-
.offererRefundPayIn((0, Utils_1.toBN)(useTimeout))
|
|
38
|
-
.accounts({
|
|
39
|
-
...accounts,
|
|
40
|
-
offererAta: ata,
|
|
41
|
-
vault: this.program._SwapVault(swapData.token),
|
|
42
|
-
vaultAuthority: this.program._SwapVaultAuthority,
|
|
43
|
-
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID
|
|
44
|
-
})
|
|
45
|
-
.instruction(), SwapRefund.CUCosts.REFUND_PAY_OUT);
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
return new SolanaAction_1.SolanaAction(signer, this.root, await this.swapProgram.methods
|
|
49
|
-
.offererRefund((0, Utils_1.toBN)(useTimeout))
|
|
50
|
-
.accounts({
|
|
51
|
-
...accounts,
|
|
52
|
-
offererUserData: this.program._SwapUserVault(swapData.offerer, swapData.token)
|
|
53
|
-
})
|
|
54
|
-
.instruction(), SwapRefund.CUCosts.REFUND);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Action for refunding with signature, adds the Ed25519 verify instruction
|
|
59
|
-
*
|
|
60
|
-
* @param signer
|
|
61
|
-
* @param swapData
|
|
62
|
-
* @param timeout
|
|
63
|
-
* @param prefix
|
|
64
|
-
* @param signature
|
|
65
|
-
* @private
|
|
66
|
-
*/
|
|
67
|
-
async RefundWithSignature(signer, swapData, timeout, prefix, signature) {
|
|
68
|
-
const action = new SolanaAction_1.SolanaAction(signer, this.root, web3_js_1.Ed25519Program.createInstructionWithPublicKey({
|
|
69
|
-
message: this.getRefundMessage(swapData, prefix, timeout),
|
|
70
|
-
publicKey: swapData.claimer.toBuffer(),
|
|
71
|
-
signature: signature
|
|
72
|
-
}), 0, undefined, undefined, true);
|
|
73
|
-
action.addAction(await this.Refund(signer, swapData, BigInt(timeout)));
|
|
74
|
-
return action;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Gets the message to be signed as a refund authorization
|
|
78
|
-
*
|
|
79
|
-
* @param swapData
|
|
80
|
-
* @param prefix
|
|
81
|
-
* @param timeout
|
|
82
|
-
* @private
|
|
83
|
-
*/
|
|
84
|
-
getRefundMessage(swapData, prefix, timeout) {
|
|
85
|
-
const messageBuffers = [
|
|
86
|
-
buffer_1.Buffer.from(prefix, "ascii"),
|
|
87
|
-
swapData.amount.toArrayLike(buffer_1.Buffer, "le", 8),
|
|
88
|
-
swapData.expiry.toArrayLike(buffer_1.Buffer, "le", 8),
|
|
89
|
-
swapData.sequence.toArrayLike(buffer_1.Buffer, "le", 8),
|
|
90
|
-
buffer_1.Buffer.from(swapData.paymentHash, "hex"),
|
|
91
|
-
new BN(timeout).toArrayLike(buffer_1.Buffer, "le", 8)
|
|
92
|
-
];
|
|
93
|
-
return buffer_1.Buffer.from((0, sha2_1.sha256)(buffer_1.Buffer.concat(messageBuffers)));
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Checks whether we should unwrap the WSOL to SOL when refunding the swap
|
|
97
|
-
*
|
|
98
|
-
* @param signer
|
|
99
|
-
* @param swapData
|
|
100
|
-
* @private
|
|
101
|
-
*/
|
|
102
|
-
shouldUnwrap(signer, swapData) {
|
|
103
|
-
return swapData.isPayIn() &&
|
|
104
|
-
swapData.token.equals(SolanaTokens_1.SolanaTokens.WSOL_ADDRESS) &&
|
|
105
|
-
signer.equals(swapData.offerer);
|
|
106
|
-
}
|
|
107
|
-
signSwapRefund(signer, swapData, authorizationTimeout) {
|
|
108
|
-
if (signer.keypair == null)
|
|
109
|
-
throw new Error("Unsupported");
|
|
110
|
-
if (!signer.getPublicKey().equals(swapData.claimer))
|
|
111
|
-
throw new Error("Invalid signer, public key mismatch!");
|
|
112
|
-
const authPrefix = "refund";
|
|
113
|
-
const authTimeout = Math.floor(Date.now() / 1000) + authorizationTimeout;
|
|
114
|
-
const messageBuffer = this.getRefundMessage(swapData, authPrefix, authTimeout.toString(10));
|
|
115
|
-
const signature = tweetnacl_1.sign.detached(messageBuffer, signer.keypair.secretKey);
|
|
116
|
-
return Promise.resolve({
|
|
117
|
-
prefix: authPrefix,
|
|
118
|
-
timeout: authTimeout.toString(10),
|
|
119
|
-
signature: buffer_1.Buffer.from(signature).toString("hex")
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
isSignatureValid(swapData, timeout, prefix, signature) {
|
|
123
|
-
if (prefix !== "refund")
|
|
124
|
-
throw new base_1.SignatureVerificationError("Invalid prefix");
|
|
125
|
-
const expiryTimestamp = BigInt(timeout);
|
|
126
|
-
const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
|
|
127
|
-
const isExpired = (expiryTimestamp - currentTimestamp) < BigInt(this.program._authGracePeriod);
|
|
128
|
-
if (isExpired)
|
|
129
|
-
throw new base_1.SignatureVerificationError("Authorization expired!");
|
|
130
|
-
const signatureBuffer = buffer_1.Buffer.from(signature, "hex");
|
|
131
|
-
const messageBuffer = this.getRefundMessage(swapData, prefix, timeout);
|
|
132
|
-
if (!tweetnacl_1.sign.detached.verify(messageBuffer, signatureBuffer, swapData.claimer.toBuffer())) {
|
|
133
|
-
throw new base_1.SignatureVerificationError("Invalid signature!");
|
|
134
|
-
}
|
|
135
|
-
return Promise.resolve(messageBuffer);
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Creates transactions required for refunding timed out swap, also unwraps WSOL to SOL
|
|
139
|
-
*
|
|
140
|
-
* @param signer
|
|
141
|
-
* @param swapData swap data to refund
|
|
142
|
-
* @param check whether to check if swap is already expired and refundable
|
|
143
|
-
* @param initAta should initialize ATA if it doesn't exist
|
|
144
|
-
* @param feeRate fee rate to be used for the transactions
|
|
145
|
-
*/
|
|
146
|
-
async txsRefund(signer, swapData, check, initAta, feeRate) {
|
|
147
|
-
if ((0, SolanaSwapProgram_1.isSwapProgramV1)(this.program.program)) {
|
|
148
|
-
//V1 only allows the offerer to refund
|
|
149
|
-
if (!swapData.isOfferer(signer.toString()))
|
|
150
|
-
throw new Error("Only offerer can refund in V1 on Solana");
|
|
151
|
-
}
|
|
152
|
-
if (check && !await this.program.isRequestRefundable(swapData.offerer.toString(), swapData)) {
|
|
153
|
-
throw new base_1.SwapDataVerificationError("Not refundable yet!");
|
|
154
|
-
}
|
|
155
|
-
let shouldInitAta = false;
|
|
156
|
-
if (swapData.isPayIn()) {
|
|
157
|
-
if (swapData.offererAta == null)
|
|
158
|
-
throw new Error("Swap data offererAta is null for payIn swap!");
|
|
159
|
-
if (!await this.root.Tokens.ataExists(swapData.offererAta)) {
|
|
160
|
-
if (!initAta)
|
|
161
|
-
throw new base_1.SwapDataVerificationError("ATA not initialized");
|
|
162
|
-
shouldInitAta = true;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
if (feeRate == null)
|
|
166
|
-
feeRate = await this.program.getRefundFeeRate(swapData);
|
|
167
|
-
const shouldUnwrap = this.shouldUnwrap(signer, swapData);
|
|
168
|
-
const action = new SolanaAction_1.SolanaAction(signer, this.root);
|
|
169
|
-
if (shouldInitAta) {
|
|
170
|
-
const initAction = this.root.Tokens.InitAta(signer, swapData.offerer, swapData.token, swapData.offererAta);
|
|
171
|
-
if (initAction == null)
|
|
172
|
-
throw new base_1.SwapDataVerificationError("Invalid claimer token account address");
|
|
173
|
-
action.addAction(initAction);
|
|
174
|
-
}
|
|
175
|
-
action.add(await this.Refund(signer, swapData));
|
|
176
|
-
if (shouldUnwrap)
|
|
177
|
-
action.add(this.root.Tokens.Unwrap(signer));
|
|
178
|
-
this.logger.debug("txsRefund(): creating claim transaction, swap: " + swapData.getClaimHash() +
|
|
179
|
-
" initializingAta: " + shouldInitAta + " unwrapping: " + shouldUnwrap);
|
|
180
|
-
return [await action.tx(feeRate)];
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Creates transactions required for refunding the swap with authorization signature, also unwraps WSOL to SOL
|
|
184
|
-
*
|
|
185
|
-
* @param signer
|
|
186
|
-
* @param swapData swap data to refund
|
|
187
|
-
* @param timeout signature timeout
|
|
188
|
-
* @param prefix signature prefix of the counterparty
|
|
189
|
-
* @param signature signature of the counterparty
|
|
190
|
-
* @param check whether to check if swap is committed before attempting refund
|
|
191
|
-
* @param initAta should initialize ATA if it doesn't exist
|
|
192
|
-
* @param feeRate fee rate to be used for the transactions
|
|
193
|
-
*/
|
|
194
|
-
async txsRefundWithAuthorization(signer, swapData, timeout, prefix, signature, check, initAta, feeRate) {
|
|
195
|
-
if ((0, SolanaSwapProgram_1.isSwapProgramV1)(this.program.program)) {
|
|
196
|
-
//V1 only allows the offerer to refund
|
|
197
|
-
if (!swapData.isOfferer(signer.toString()))
|
|
198
|
-
throw new Error("Only offerer can refund in V1 on Solana");
|
|
199
|
-
}
|
|
200
|
-
if (check && !await this.program.isCommited(swapData)) {
|
|
201
|
-
throw new base_1.SwapDataVerificationError("Not correctly committed");
|
|
202
|
-
}
|
|
203
|
-
await this.isSignatureValid(swapData, timeout, prefix, signature);
|
|
204
|
-
let shouldInitAta = false;
|
|
205
|
-
if (swapData.isPayIn()) {
|
|
206
|
-
if (swapData.offererAta == null)
|
|
207
|
-
throw new Error("Swap data offererAta is null for payIn swap!");
|
|
208
|
-
if (!await this.root.Tokens.ataExists(swapData.offererAta)) {
|
|
209
|
-
if (!initAta)
|
|
210
|
-
throw new base_1.SwapDataVerificationError("ATA not initialized");
|
|
211
|
-
shouldInitAta = true;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
if (feeRate == null)
|
|
215
|
-
feeRate = await this.program.getRefundFeeRate(swapData);
|
|
216
|
-
const signatureBuffer = buffer_1.Buffer.from(signature, "hex");
|
|
217
|
-
const shouldUnwrap = this.shouldUnwrap(signer, swapData);
|
|
218
|
-
const action = await this.RefundWithSignature(signer, swapData, timeout, prefix, signatureBuffer);
|
|
219
|
-
if (shouldInitAta) {
|
|
220
|
-
const initAction = this.root.Tokens.InitAta(signer, swapData.offerer, swapData.token, swapData.offererAta);
|
|
221
|
-
if (initAction == null)
|
|
222
|
-
throw new base_1.SwapDataVerificationError("Invalid claimer token account address");
|
|
223
|
-
action.addAction(initAction, 1); //Need to add it after the Ed25519 verify IX, but before the actual refund IX
|
|
224
|
-
}
|
|
225
|
-
if (shouldUnwrap)
|
|
226
|
-
action.add(this.root.Tokens.Unwrap(signer));
|
|
227
|
-
this.logger.debug("txsRefundWithAuthorization(): creating claim transaction, swap: " + swapData.getClaimHash() +
|
|
228
|
-
" initializingAta: " + shouldInitAta + " unwrapping: " + shouldUnwrap +
|
|
229
|
-
" auth expiry: " + timeout + " signature: " + signature);
|
|
230
|
-
//Push a random keypair to the TX signer such that pesky Phantom
|
|
231
|
-
// doesn't fuck up the instructions order!
|
|
232
|
-
const tx = await action.tx(feeRate);
|
|
233
|
-
const _signer = web3_js_1.Keypair.generate();
|
|
234
|
-
const ix = tx.tx.instructions.find(val => val.programId.equals(this.program.program.programId));
|
|
235
|
-
if (ix != null) {
|
|
236
|
-
ix.keys.push({
|
|
237
|
-
pubkey: _signer.publicKey,
|
|
238
|
-
isSigner: true,
|
|
239
|
-
isWritable: false
|
|
240
|
-
});
|
|
241
|
-
(tx.signers ?? (tx.signers = [])).push(_signer);
|
|
242
|
-
}
|
|
243
|
-
return [tx];
|
|
244
|
-
}
|
|
245
|
-
getRefundFeeRate(swapData) {
|
|
246
|
-
const accounts = [];
|
|
247
|
-
if (swapData.payIn) {
|
|
248
|
-
if (swapData.token != null)
|
|
249
|
-
accounts.push(this.program._SwapVault(swapData.token));
|
|
250
|
-
if (swapData.offerer != null)
|
|
251
|
-
accounts.push(swapData.offerer);
|
|
252
|
-
if (swapData.claimer != null)
|
|
253
|
-
accounts.push(swapData.claimer);
|
|
254
|
-
if (swapData.offererAta != null && !swapData.offererAta.equals(web3_js_1.PublicKey.default))
|
|
255
|
-
accounts.push(swapData.offererAta);
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
if (swapData.offerer != null) {
|
|
259
|
-
accounts.push(swapData.offerer);
|
|
260
|
-
if (swapData.token != null)
|
|
261
|
-
accounts.push(this.program._SwapUserVault(swapData.offerer, swapData.token));
|
|
262
|
-
}
|
|
263
|
-
if (swapData.claimer != null)
|
|
264
|
-
accounts.push(swapData.claimer);
|
|
265
|
-
}
|
|
266
|
-
if (swapData.paymentHash != null)
|
|
267
|
-
accounts.push(this.program._SwapEscrowState(buffer_1.Buffer.from(swapData.paymentHash, "hex")));
|
|
268
|
-
return this.root.Fees.getFeeRate(accounts);
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Get the estimated solana transaction fee of the refund transaction, in the worst case scenario in case where the
|
|
272
|
-
* ATA needs to be initialized again (i.e. adding the ATA rent exempt lamports to the fee)
|
|
273
|
-
*/
|
|
274
|
-
async getRefundFee(swapData, feeRate) {
|
|
275
|
-
return BigInt(swapData == null || swapData.payIn ? SolanaTokens_1.SolanaTokens.SPL_ATA_RENT_EXEMPT : 0) +
|
|
276
|
-
await this.getRawRefundFee(swapData, feeRate);
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Get the estimated solana transaction fee of the refund transaction
|
|
280
|
-
*/
|
|
281
|
-
async getRawRefundFee(swapData, feeRate) {
|
|
282
|
-
if (swapData == null)
|
|
283
|
-
return 15000n;
|
|
284
|
-
feeRate = feeRate || await this.getRefundFeeRate(swapData);
|
|
285
|
-
const computeBudget = swapData.payIn ? SwapRefund.CUCosts.REFUND_PAY_OUT : SwapRefund.CUCosts.REFUND;
|
|
286
|
-
return 15000n + this.root.Fees.getPriorityFee(computeBudget, feeRate);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
exports.SwapRefund = SwapRefund;
|
|
290
|
-
SwapRefund.CUCosts = {
|
|
291
|
-
REFUND: 15000,
|
|
292
|
-
REFUND_PAY_OUT: 50000
|
|
293
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SwapRefund = void 0;
|
|
4
|
+
const SolanaSwapModule_1 = require("../SolanaSwapModule");
|
|
5
|
+
const sha2_1 = require("@noble/hashes/sha2");
|
|
6
|
+
const tweetnacl_1 = require("tweetnacl");
|
|
7
|
+
const base_1 = require("@atomiqlabs/base");
|
|
8
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
9
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
10
|
+
const SolanaAction_1 = require("../../chain/SolanaAction");
|
|
11
|
+
const Utils_1 = require("../../../utils/Utils");
|
|
12
|
+
const buffer_1 = require("buffer");
|
|
13
|
+
const SolanaTokens_1 = require("../../chain/modules/SolanaTokens");
|
|
14
|
+
const BN = require("bn.js");
|
|
15
|
+
const SolanaSwapProgram_1 = require("../SolanaSwapProgram");
|
|
16
|
+
class SwapRefund extends SolanaSwapModule_1.SolanaSwapModule {
|
|
17
|
+
/**
|
|
18
|
+
* Action for generic Refund instruction
|
|
19
|
+
*
|
|
20
|
+
* @param signer
|
|
21
|
+
* @param swapData
|
|
22
|
+
* @param refundAuthTimeout optional refund authorization timeout (should be 0 for refunding expired swaps)
|
|
23
|
+
* @private
|
|
24
|
+
*/
|
|
25
|
+
async Refund(signer, swapData, refundAuthTimeout) {
|
|
26
|
+
const accounts = {
|
|
27
|
+
offerer: swapData.offerer,
|
|
28
|
+
claimer: swapData.claimer,
|
|
29
|
+
escrowState: this.program._SwapEscrowState(buffer_1.Buffer.from(swapData.paymentHash, "hex")),
|
|
30
|
+
claimerUserData: !swapData.payOut ? this.program._SwapUserVault(swapData.claimer, swapData.token) : null,
|
|
31
|
+
ixSysvar: refundAuthTimeout != null ? web3_js_1.SYSVAR_INSTRUCTIONS_PUBKEY : null
|
|
32
|
+
};
|
|
33
|
+
const useTimeout = refundAuthTimeout != null ? refundAuthTimeout : 0n;
|
|
34
|
+
if (swapData.isPayIn()) {
|
|
35
|
+
const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(swapData.token, swapData.offerer);
|
|
36
|
+
return new SolanaAction_1.SolanaAction(signer, this.root, await this.swapProgram.methods
|
|
37
|
+
.offererRefundPayIn((0, Utils_1.toBN)(useTimeout))
|
|
38
|
+
.accounts({
|
|
39
|
+
...accounts,
|
|
40
|
+
offererAta: ata,
|
|
41
|
+
vault: this.program._SwapVault(swapData.token),
|
|
42
|
+
vaultAuthority: this.program._SwapVaultAuthority,
|
|
43
|
+
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID
|
|
44
|
+
})
|
|
45
|
+
.instruction(), SwapRefund.CUCosts.REFUND_PAY_OUT);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
return new SolanaAction_1.SolanaAction(signer, this.root, await this.swapProgram.methods
|
|
49
|
+
.offererRefund((0, Utils_1.toBN)(useTimeout))
|
|
50
|
+
.accounts({
|
|
51
|
+
...accounts,
|
|
52
|
+
offererUserData: this.program._SwapUserVault(swapData.offerer, swapData.token)
|
|
53
|
+
})
|
|
54
|
+
.instruction(), SwapRefund.CUCosts.REFUND);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Action for refunding with signature, adds the Ed25519 verify instruction
|
|
59
|
+
*
|
|
60
|
+
* @param signer
|
|
61
|
+
* @param swapData
|
|
62
|
+
* @param timeout
|
|
63
|
+
* @param prefix
|
|
64
|
+
* @param signature
|
|
65
|
+
* @private
|
|
66
|
+
*/
|
|
67
|
+
async RefundWithSignature(signer, swapData, timeout, prefix, signature) {
|
|
68
|
+
const action = new SolanaAction_1.SolanaAction(signer, this.root, web3_js_1.Ed25519Program.createInstructionWithPublicKey({
|
|
69
|
+
message: this.getRefundMessage(swapData, prefix, timeout),
|
|
70
|
+
publicKey: swapData.claimer.toBuffer(),
|
|
71
|
+
signature: signature
|
|
72
|
+
}), 0, undefined, undefined, true);
|
|
73
|
+
action.addAction(await this.Refund(signer, swapData, BigInt(timeout)));
|
|
74
|
+
return action;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Gets the message to be signed as a refund authorization
|
|
78
|
+
*
|
|
79
|
+
* @param swapData
|
|
80
|
+
* @param prefix
|
|
81
|
+
* @param timeout
|
|
82
|
+
* @private
|
|
83
|
+
*/
|
|
84
|
+
getRefundMessage(swapData, prefix, timeout) {
|
|
85
|
+
const messageBuffers = [
|
|
86
|
+
buffer_1.Buffer.from(prefix, "ascii"),
|
|
87
|
+
swapData.amount.toArrayLike(buffer_1.Buffer, "le", 8),
|
|
88
|
+
swapData.expiry.toArrayLike(buffer_1.Buffer, "le", 8),
|
|
89
|
+
swapData.sequence.toArrayLike(buffer_1.Buffer, "le", 8),
|
|
90
|
+
buffer_1.Buffer.from(swapData.paymentHash, "hex"),
|
|
91
|
+
new BN(timeout).toArrayLike(buffer_1.Buffer, "le", 8)
|
|
92
|
+
];
|
|
93
|
+
return buffer_1.Buffer.from((0, sha2_1.sha256)(buffer_1.Buffer.concat(messageBuffers)));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Checks whether we should unwrap the WSOL to SOL when refunding the swap
|
|
97
|
+
*
|
|
98
|
+
* @param signer
|
|
99
|
+
* @param swapData
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
shouldUnwrap(signer, swapData) {
|
|
103
|
+
return swapData.isPayIn() &&
|
|
104
|
+
swapData.token.equals(SolanaTokens_1.SolanaTokens.WSOL_ADDRESS) &&
|
|
105
|
+
signer.equals(swapData.offerer);
|
|
106
|
+
}
|
|
107
|
+
signSwapRefund(signer, swapData, authorizationTimeout) {
|
|
108
|
+
if (signer.keypair == null)
|
|
109
|
+
throw new Error("Unsupported");
|
|
110
|
+
if (!signer.getPublicKey().equals(swapData.claimer))
|
|
111
|
+
throw new Error("Invalid signer, public key mismatch!");
|
|
112
|
+
const authPrefix = "refund";
|
|
113
|
+
const authTimeout = Math.floor(Date.now() / 1000) + authorizationTimeout;
|
|
114
|
+
const messageBuffer = this.getRefundMessage(swapData, authPrefix, authTimeout.toString(10));
|
|
115
|
+
const signature = tweetnacl_1.sign.detached(messageBuffer, signer.keypair.secretKey);
|
|
116
|
+
return Promise.resolve({
|
|
117
|
+
prefix: authPrefix,
|
|
118
|
+
timeout: authTimeout.toString(10),
|
|
119
|
+
signature: buffer_1.Buffer.from(signature).toString("hex")
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
isSignatureValid(swapData, timeout, prefix, signature) {
|
|
123
|
+
if (prefix !== "refund")
|
|
124
|
+
throw new base_1.SignatureVerificationError("Invalid prefix");
|
|
125
|
+
const expiryTimestamp = BigInt(timeout);
|
|
126
|
+
const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
|
|
127
|
+
const isExpired = (expiryTimestamp - currentTimestamp) < BigInt(this.program._authGracePeriod);
|
|
128
|
+
if (isExpired)
|
|
129
|
+
throw new base_1.SignatureVerificationError("Authorization expired!");
|
|
130
|
+
const signatureBuffer = buffer_1.Buffer.from(signature, "hex");
|
|
131
|
+
const messageBuffer = this.getRefundMessage(swapData, prefix, timeout);
|
|
132
|
+
if (!tweetnacl_1.sign.detached.verify(messageBuffer, signatureBuffer, swapData.claimer.toBuffer())) {
|
|
133
|
+
throw new base_1.SignatureVerificationError("Invalid signature!");
|
|
134
|
+
}
|
|
135
|
+
return Promise.resolve(messageBuffer);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Creates transactions required for refunding timed out swap, also unwraps WSOL to SOL
|
|
139
|
+
*
|
|
140
|
+
* @param signer
|
|
141
|
+
* @param swapData swap data to refund
|
|
142
|
+
* @param check whether to check if swap is already expired and refundable
|
|
143
|
+
* @param initAta should initialize ATA if it doesn't exist
|
|
144
|
+
* @param feeRate fee rate to be used for the transactions
|
|
145
|
+
*/
|
|
146
|
+
async txsRefund(signer, swapData, check, initAta, feeRate) {
|
|
147
|
+
if ((0, SolanaSwapProgram_1.isSwapProgramV1)(this.program.program)) {
|
|
148
|
+
//V1 only allows the offerer to refund
|
|
149
|
+
if (!swapData.isOfferer(signer.toString()))
|
|
150
|
+
throw new Error("Only offerer can refund in V1 on Solana");
|
|
151
|
+
}
|
|
152
|
+
if (check && !await this.program.isRequestRefundable(swapData.offerer.toString(), swapData)) {
|
|
153
|
+
throw new base_1.SwapDataVerificationError("Not refundable yet!");
|
|
154
|
+
}
|
|
155
|
+
let shouldInitAta = false;
|
|
156
|
+
if (swapData.isPayIn()) {
|
|
157
|
+
if (swapData.offererAta == null)
|
|
158
|
+
throw new Error("Swap data offererAta is null for payIn swap!");
|
|
159
|
+
if (!await this.root.Tokens.ataExists(swapData.offererAta)) {
|
|
160
|
+
if (!initAta)
|
|
161
|
+
throw new base_1.SwapDataVerificationError("ATA not initialized");
|
|
162
|
+
shouldInitAta = true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (feeRate == null)
|
|
166
|
+
feeRate = await this.program.getRefundFeeRate(swapData);
|
|
167
|
+
const shouldUnwrap = this.shouldUnwrap(signer, swapData);
|
|
168
|
+
const action = new SolanaAction_1.SolanaAction(signer, this.root);
|
|
169
|
+
if (shouldInitAta) {
|
|
170
|
+
const initAction = this.root.Tokens.InitAta(signer, swapData.offerer, swapData.token, swapData.offererAta);
|
|
171
|
+
if (initAction == null)
|
|
172
|
+
throw new base_1.SwapDataVerificationError("Invalid claimer token account address");
|
|
173
|
+
action.addAction(initAction);
|
|
174
|
+
}
|
|
175
|
+
action.add(await this.Refund(signer, swapData));
|
|
176
|
+
if (shouldUnwrap)
|
|
177
|
+
action.add(this.root.Tokens.Unwrap(signer));
|
|
178
|
+
this.logger.debug("txsRefund(): creating claim transaction, swap: " + swapData.getClaimHash() +
|
|
179
|
+
" initializingAta: " + shouldInitAta + " unwrapping: " + shouldUnwrap);
|
|
180
|
+
return [await action.tx(feeRate)];
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Creates transactions required for refunding the swap with authorization signature, also unwraps WSOL to SOL
|
|
184
|
+
*
|
|
185
|
+
* @param signer
|
|
186
|
+
* @param swapData swap data to refund
|
|
187
|
+
* @param timeout signature timeout
|
|
188
|
+
* @param prefix signature prefix of the counterparty
|
|
189
|
+
* @param signature signature of the counterparty
|
|
190
|
+
* @param check whether to check if swap is committed before attempting refund
|
|
191
|
+
* @param initAta should initialize ATA if it doesn't exist
|
|
192
|
+
* @param feeRate fee rate to be used for the transactions
|
|
193
|
+
*/
|
|
194
|
+
async txsRefundWithAuthorization(signer, swapData, timeout, prefix, signature, check, initAta, feeRate) {
|
|
195
|
+
if ((0, SolanaSwapProgram_1.isSwapProgramV1)(this.program.program)) {
|
|
196
|
+
//V1 only allows the offerer to refund
|
|
197
|
+
if (!swapData.isOfferer(signer.toString()))
|
|
198
|
+
throw new Error("Only offerer can refund in V1 on Solana");
|
|
199
|
+
}
|
|
200
|
+
if (check && !await this.program.isCommited(swapData)) {
|
|
201
|
+
throw new base_1.SwapDataVerificationError("Not correctly committed");
|
|
202
|
+
}
|
|
203
|
+
await this.isSignatureValid(swapData, timeout, prefix, signature);
|
|
204
|
+
let shouldInitAta = false;
|
|
205
|
+
if (swapData.isPayIn()) {
|
|
206
|
+
if (swapData.offererAta == null)
|
|
207
|
+
throw new Error("Swap data offererAta is null for payIn swap!");
|
|
208
|
+
if (!await this.root.Tokens.ataExists(swapData.offererAta)) {
|
|
209
|
+
if (!initAta)
|
|
210
|
+
throw new base_1.SwapDataVerificationError("ATA not initialized");
|
|
211
|
+
shouldInitAta = true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (feeRate == null)
|
|
215
|
+
feeRate = await this.program.getRefundFeeRate(swapData);
|
|
216
|
+
const signatureBuffer = buffer_1.Buffer.from(signature, "hex");
|
|
217
|
+
const shouldUnwrap = this.shouldUnwrap(signer, swapData);
|
|
218
|
+
const action = await this.RefundWithSignature(signer, swapData, timeout, prefix, signatureBuffer);
|
|
219
|
+
if (shouldInitAta) {
|
|
220
|
+
const initAction = this.root.Tokens.InitAta(signer, swapData.offerer, swapData.token, swapData.offererAta);
|
|
221
|
+
if (initAction == null)
|
|
222
|
+
throw new base_1.SwapDataVerificationError("Invalid claimer token account address");
|
|
223
|
+
action.addAction(initAction, 1); //Need to add it after the Ed25519 verify IX, but before the actual refund IX
|
|
224
|
+
}
|
|
225
|
+
if (shouldUnwrap)
|
|
226
|
+
action.add(this.root.Tokens.Unwrap(signer));
|
|
227
|
+
this.logger.debug("txsRefundWithAuthorization(): creating claim transaction, swap: " + swapData.getClaimHash() +
|
|
228
|
+
" initializingAta: " + shouldInitAta + " unwrapping: " + shouldUnwrap +
|
|
229
|
+
" auth expiry: " + timeout + " signature: " + signature);
|
|
230
|
+
//Push a random keypair to the TX signer such that pesky Phantom
|
|
231
|
+
// doesn't fuck up the instructions order!
|
|
232
|
+
const tx = await action.tx(feeRate);
|
|
233
|
+
const _signer = web3_js_1.Keypair.generate();
|
|
234
|
+
const ix = tx.tx.instructions.find(val => val.programId.equals(this.program.program.programId));
|
|
235
|
+
if (ix != null) {
|
|
236
|
+
ix.keys.push({
|
|
237
|
+
pubkey: _signer.publicKey,
|
|
238
|
+
isSigner: true,
|
|
239
|
+
isWritable: false
|
|
240
|
+
});
|
|
241
|
+
(tx.signers ?? (tx.signers = [])).push(_signer);
|
|
242
|
+
}
|
|
243
|
+
return [tx];
|
|
244
|
+
}
|
|
245
|
+
getRefundFeeRate(swapData) {
|
|
246
|
+
const accounts = [];
|
|
247
|
+
if (swapData.payIn) {
|
|
248
|
+
if (swapData.token != null)
|
|
249
|
+
accounts.push(this.program._SwapVault(swapData.token));
|
|
250
|
+
if (swapData.offerer != null)
|
|
251
|
+
accounts.push(swapData.offerer);
|
|
252
|
+
if (swapData.claimer != null)
|
|
253
|
+
accounts.push(swapData.claimer);
|
|
254
|
+
if (swapData.offererAta != null && !swapData.offererAta.equals(web3_js_1.PublicKey.default))
|
|
255
|
+
accounts.push(swapData.offererAta);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
if (swapData.offerer != null) {
|
|
259
|
+
accounts.push(swapData.offerer);
|
|
260
|
+
if (swapData.token != null)
|
|
261
|
+
accounts.push(this.program._SwapUserVault(swapData.offerer, swapData.token));
|
|
262
|
+
}
|
|
263
|
+
if (swapData.claimer != null)
|
|
264
|
+
accounts.push(swapData.claimer);
|
|
265
|
+
}
|
|
266
|
+
if (swapData.paymentHash != null)
|
|
267
|
+
accounts.push(this.program._SwapEscrowState(buffer_1.Buffer.from(swapData.paymentHash, "hex")));
|
|
268
|
+
return this.root.Fees.getFeeRate(accounts);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Get the estimated solana transaction fee of the refund transaction, in the worst case scenario in case where the
|
|
272
|
+
* ATA needs to be initialized again (i.e. adding the ATA rent exempt lamports to the fee)
|
|
273
|
+
*/
|
|
274
|
+
async getRefundFee(swapData, feeRate) {
|
|
275
|
+
return BigInt(swapData == null || swapData.payIn ? SolanaTokens_1.SolanaTokens.SPL_ATA_RENT_EXEMPT : 0) +
|
|
276
|
+
await this.getRawRefundFee(swapData, feeRate);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Get the estimated solana transaction fee of the refund transaction
|
|
280
|
+
*/
|
|
281
|
+
async getRawRefundFee(swapData, feeRate) {
|
|
282
|
+
if (swapData == null)
|
|
283
|
+
return 15000n;
|
|
284
|
+
feeRate = feeRate || await this.getRefundFeeRate(swapData);
|
|
285
|
+
const computeBudget = swapData.payIn ? SwapRefund.CUCosts.REFUND_PAY_OUT : SwapRefund.CUCosts.REFUND;
|
|
286
|
+
return 15000n + this.root.Fees.getPriorityFee(computeBudget, feeRate);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
exports.SwapRefund = SwapRefund;
|
|
290
|
+
SwapRefund.CUCosts = {
|
|
291
|
+
REFUND: 15000,
|
|
292
|
+
REFUND_PAY_OUT: 50000
|
|
293
|
+
};
|