@xchainjs/xchain-mayachain-amm 0.1.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 +21 -0
- package/README.md +70 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.esm.js +402 -0
- package/lib/index.js +407 -0
- package/lib/mayachain-amm.d.ts +57 -0
- package/lib/types.d.ts +4 -0
- package/lib/wallet.d.ts +112 -0
- package/package.json +97 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 THORChain
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# `@xchainjs/xchain-mayachain-amm`
|
|
2
|
+
|
|
3
|
+
## Modules
|
|
4
|
+
|
|
5
|
+
Mayachain AMM module
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
yarn add @xchainjs/xchain-mayachain-amm
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Following peer dependencies have to be installed into your project. These are not included in `@xchainjs/xchain-mayachain-amm`.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@cosmos-client/core": "0.46.1",
|
|
18
|
+
"@psf/bitcoincashjs-lib": "^4.0.3",
|
|
19
|
+
"@xchainjs/xchain-bitcoin": "^0.23.6",
|
|
20
|
+
"@xchainjs/xchain-client": "^0.15.4",
|
|
21
|
+
"@xchainjs/xchain-cosmos-sdk": "^0.1.3",
|
|
22
|
+
"@xchainjs/xchain-crypto": "^0.3.0",
|
|
23
|
+
"@xchainjs/xchain-dash": "^0.2.5",
|
|
24
|
+
"@xchainjs/xchain-ethereum": "^0.31.1",
|
|
25
|
+
"@xchainjs/xchain-mayanode": "^0.1.2",
|
|
26
|
+
"@xchainjs/xchain-mayachain-query": "^0.1.0",
|
|
27
|
+
"@xchainjs/xchain-evm": "^0.4.1",
|
|
28
|
+
"@xchainjs/xchain-kujira": "^0.1.2",
|
|
29
|
+
"@xchainjs/xchain-mayachain": "^0.2.10",
|
|
30
|
+
"@xchainjs/xchain-mayamidgard": "^0.1.0",
|
|
31
|
+
"@xchainjs/xchain-thorchain": "^0.28.13",
|
|
32
|
+
"@xchainjs/xchain-util": "^0.13.1",
|
|
33
|
+
"@xchainjs/xchain-utxo-providers": "^0.2.8",
|
|
34
|
+
"axios": "^1.3.6",
|
|
35
|
+
"axios-retry": "^3.2.5",
|
|
36
|
+
"bignumber.js": "^9.0.0",
|
|
37
|
+
"bitcoinjs-lib": "5.2.0",
|
|
38
|
+
"coininfo": "^5.1.0",
|
|
39
|
+
"coinselect": "^3.1.12",
|
|
40
|
+
"dotenv": "^16.0.0",
|
|
41
|
+
"ethers": "^5.6.6",
|
|
42
|
+
"wif": "^2.0.6"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## For live examples
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## Documentation
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## Compiler options
|
|
54
|
+
|
|
55
|
+
tsconfig compiler options
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
{
|
|
59
|
+
"compilerOptions": {
|
|
60
|
+
"module":"commonjs",
|
|
61
|
+
"target": "es5",
|
|
62
|
+
"noEmitOnError": true,
|
|
63
|
+
"resolveJsonModule": true,
|
|
64
|
+
"esModuleInterop": true,
|
|
65
|
+
"lib": ["es6", "dom", "es2016", "es2017"]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Setting environment variables
|
package/lib/index.d.ts
ADDED
package/lib/index.esm.js
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import { Client, defaultBTCParams, BTCChain } from '@xchainjs/xchain-bitcoin';
|
|
2
|
+
import { Client as Client$2, defaultDashParams, DASHChain } from '@xchainjs/xchain-dash';
|
|
3
|
+
import { Client as Client$1, defaultEthParams, ETHChain, AssetETH } from '@xchainjs/xchain-ethereum';
|
|
4
|
+
import { isApproved, abi } from '@xchainjs/xchain-evm';
|
|
5
|
+
import { Client as Client$3, defaultKujiParams, KUJIChain } from '@xchainjs/xchain-kujira';
|
|
6
|
+
import { Client as Client$5, MAYAChain } from '@xchainjs/xchain-mayachain';
|
|
7
|
+
import { Client as Client$4, THORChain } from '@xchainjs/xchain-thorchain';
|
|
8
|
+
import { assetToString, getContractAddressFromAsset, CryptoAmount, baseAmount } from '@xchainjs/xchain-util';
|
|
9
|
+
import { Network } from '@xchainjs/xchain-client';
|
|
10
|
+
import { MayachainQuery } from '@xchainjs/xchain-mayachain-query';
|
|
11
|
+
import { ethers } from 'ethers';
|
|
12
|
+
|
|
13
|
+
/******************************************************************************
|
|
14
|
+
Copyright (c) Microsoft Corporation.
|
|
15
|
+
|
|
16
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
17
|
+
purpose with or without fee is hereby granted.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
20
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
21
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
22
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
23
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
24
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
25
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
26
|
+
***************************************************************************** */
|
|
27
|
+
|
|
28
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
29
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
30
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
31
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
32
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
33
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
34
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class Wallet {
|
|
39
|
+
/**
|
|
40
|
+
* Constructor to create a Wallet
|
|
41
|
+
*
|
|
42
|
+
* @param {string} phrase - mnemonic phrase
|
|
43
|
+
* @param {Network} network - mnemonic phrase
|
|
44
|
+
* @param {ChainConfigs} chainConfigs - Config by chain. If it isn not set, each client uses its default params
|
|
45
|
+
* @returns Wallet
|
|
46
|
+
*/
|
|
47
|
+
constructor(phrase, network, chainConfigs = {}) {
|
|
48
|
+
const settings = { network, phrase };
|
|
49
|
+
this.clients = {
|
|
50
|
+
BTC: new Client(Object.assign(Object.assign(Object.assign({}, defaultBTCParams), chainConfigs[BTCChain]), settings)),
|
|
51
|
+
ETH: new Client$1(Object.assign(Object.assign(Object.assign({}, defaultEthParams), chainConfigs[ETHChain]), settings)),
|
|
52
|
+
DASH: new Client$2(Object.assign(Object.assign(Object.assign({}, defaultDashParams), chainConfigs[DASHChain]), settings)),
|
|
53
|
+
KUJI: new Client$3(Object.assign(Object.assign(Object.assign({}, defaultKujiParams), chainConfigs[KUJIChain]), settings)),
|
|
54
|
+
THOR: new Client$4(Object.assign(Object.assign({}, chainConfigs[THORChain]), settings)),
|
|
55
|
+
MAYA: new Client$5(Object.assign(Object.assign({}, chainConfigs[MAYAChain]), settings)),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get chain address
|
|
60
|
+
* @param {Chain} chain
|
|
61
|
+
* @returns the chain address
|
|
62
|
+
*/
|
|
63
|
+
getAddress(chain) {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
const client = this.getChainClient(chain);
|
|
66
|
+
return client.getAddressAsync();
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check if address is valid
|
|
71
|
+
* @param {Chain} chain in which the address has to be valid
|
|
72
|
+
* @param {string} address to validate
|
|
73
|
+
* @returns true if it is a valid address, otherwise, false
|
|
74
|
+
*/
|
|
75
|
+
validateAddress(chain, address) {
|
|
76
|
+
const client = this.getChainClient(chain);
|
|
77
|
+
return client.validateAddress(address);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Make a transaction
|
|
81
|
+
* @param {TxParams} txParams to make the transfer
|
|
82
|
+
* @returns the transaction hash
|
|
83
|
+
*/
|
|
84
|
+
transfer({ asset, amount, recipient, memo, walletIndex }) {
|
|
85
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
86
|
+
const client = this.getChainClient(asset.chain);
|
|
87
|
+
return client.transfer({
|
|
88
|
+
walletIndex,
|
|
89
|
+
asset,
|
|
90
|
+
amount,
|
|
91
|
+
recipient,
|
|
92
|
+
memo,
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Make a deposit
|
|
98
|
+
* @param {DepositParam} depositParams
|
|
99
|
+
* @returns the hash of the deposit
|
|
100
|
+
* @throws {Error} if can not make deposit with the asset
|
|
101
|
+
*/
|
|
102
|
+
deposit({ asset, amount, memo }) {
|
|
103
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
const client = this.getChainClient(asset.chain);
|
|
105
|
+
if (!('deposit' in client))
|
|
106
|
+
throw Error(`Can not deposit with ${asset.chain} client`);
|
|
107
|
+
return client.deposit({ asset, amount, memo });
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Check if an spenderAddress is allowed to spend in name of another address certain asset amount
|
|
112
|
+
* @param {Asset} asset to check
|
|
113
|
+
* @param {BaseAmount} amount to check
|
|
114
|
+
* @param {string} fromAddress owner of the amount asset
|
|
115
|
+
* @param {string} spenderAddress spender to check if it is allowed to spend
|
|
116
|
+
* @returns true if the spenderAddress is allowed to spend the amount, otherwise, false
|
|
117
|
+
* @throws {Error} if asset is a non ERC20 asset
|
|
118
|
+
*/
|
|
119
|
+
isApproved(asset, amount, fromAddress, spenderAddress) {
|
|
120
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
121
|
+
const client = this.getChainClient(asset.chain);
|
|
122
|
+
if (!this.isERC20Asset(asset))
|
|
123
|
+
throw Error(`${assetToString(asset)} is not an ERC20 token`);
|
|
124
|
+
const contractAddress = getContractAddressFromAsset(asset);
|
|
125
|
+
return isApproved({
|
|
126
|
+
provider: client.getProvider(),
|
|
127
|
+
amount,
|
|
128
|
+
spenderAddress,
|
|
129
|
+
contractAddress,
|
|
130
|
+
fromAddress,
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get transaction url
|
|
136
|
+
* @param {Chain} chain of the transaction
|
|
137
|
+
* @param {string} hash of the transaction
|
|
138
|
+
* @returns the transaction url
|
|
139
|
+
*/
|
|
140
|
+
getExplorerTxUrl(chain, hash) {
|
|
141
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
142
|
+
const client = this.getChainClient(chain);
|
|
143
|
+
return client.getExplorerTxUrl(hash);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get feeRates
|
|
148
|
+
* @param {Chain} chain of which return the feeRates
|
|
149
|
+
* @returns the gas fee rates
|
|
150
|
+
* @throws {Error} if gas fee rates can not be returned from the chain
|
|
151
|
+
*/
|
|
152
|
+
getGasFeeRates(chain) {
|
|
153
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
154
|
+
const client = this.getChainClient(chain);
|
|
155
|
+
if (!('estimateGasPrices' in client))
|
|
156
|
+
throw Error(`Can not get gas with ${chain} client`);
|
|
157
|
+
return client.estimateGasPrices();
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Get chain wallet
|
|
162
|
+
* @param {Chain} chain of which return the wallet
|
|
163
|
+
* @returns the chain wallet
|
|
164
|
+
* @throws {Error} wallet can not be retrieve from chain
|
|
165
|
+
*/
|
|
166
|
+
getChainWallet(chain) {
|
|
167
|
+
const client = this.getChainClient(chain);
|
|
168
|
+
if (!('getWallet' in client))
|
|
169
|
+
throw Error(`Can not get wallet of ${chain} client`);
|
|
170
|
+
return client.getWallet();
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get chain client
|
|
174
|
+
* @param {Chain} chain of which return the client
|
|
175
|
+
* @returns the chain client
|
|
176
|
+
* @throws {Error} if client does not exist
|
|
177
|
+
*/
|
|
178
|
+
getChainClient(chain) {
|
|
179
|
+
const client = this.clients[chain];
|
|
180
|
+
if (!client)
|
|
181
|
+
throw Error(`Client not found for ${chain} chain`);
|
|
182
|
+
return client;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Check if asset is ERC20
|
|
186
|
+
* @param {Asset} asset to check
|
|
187
|
+
* @returns true if asset is ERC20, otherwise, false
|
|
188
|
+
*/
|
|
189
|
+
isERC20Asset(asset) {
|
|
190
|
+
const isGasAsset = [AssetETH.symbol].includes(asset.symbol);
|
|
191
|
+
return this.isEVMChain(asset.chain) && !isGasAsset;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Check if asset chain is EVM
|
|
195
|
+
* @param {Chain} chain to check
|
|
196
|
+
* @returns true if chain is EVM, otherwise, false
|
|
197
|
+
*/
|
|
198
|
+
isEVMChain(chain) {
|
|
199
|
+
return [AssetETH.chain].includes(chain);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* MAYAChainAMM class for interacting with THORChain.
|
|
205
|
+
* Recommended main class to use for swapping with MAYAChain
|
|
206
|
+
* Has access to Midgard and MayaNode data
|
|
207
|
+
*/
|
|
208
|
+
class MayachainAMM {
|
|
209
|
+
/**
|
|
210
|
+
* Constructor to create a MayachainAMM
|
|
211
|
+
* @param mayachainQuery - an instance of the MayachainQuery
|
|
212
|
+
* @returns MayachainAMM
|
|
213
|
+
*/
|
|
214
|
+
constructor(mayachainQuery = new MayachainQuery(), wallet = new Wallet('', Network.Mainnet)) {
|
|
215
|
+
this.mayachainQuery = mayachainQuery;
|
|
216
|
+
this.wallet = wallet;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Estimate swap validating the swap params
|
|
220
|
+
* @param {QuoteSwapParams} quoteSwapParams Swap params
|
|
221
|
+
* @returns {QuoteSwap} Quote swap. If swap can not be done, it returns an empty QuoteSwap with the reasons the swap can not be done
|
|
222
|
+
*/
|
|
223
|
+
estimateSwap({ fromAsset, fromAddress, amount, destinationAsset, destinationAddress, affiliateAddress, affiliateBps, toleranceBps, }) {
|
|
224
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
225
|
+
const errors = yield this.validateSwap({
|
|
226
|
+
fromAsset,
|
|
227
|
+
fromAddress,
|
|
228
|
+
amount,
|
|
229
|
+
destinationAsset,
|
|
230
|
+
destinationAddress,
|
|
231
|
+
affiliateAddress,
|
|
232
|
+
affiliateBps,
|
|
233
|
+
});
|
|
234
|
+
if (errors.length > 0) {
|
|
235
|
+
return {
|
|
236
|
+
toAddress: ``,
|
|
237
|
+
memo: ``,
|
|
238
|
+
expectedAmount: new CryptoAmount(baseAmount(0), destinationAsset),
|
|
239
|
+
dustThreshold: this.mayachainQuery.getChainDustValue(fromAsset.chain),
|
|
240
|
+
fees: {
|
|
241
|
+
asset: destinationAsset,
|
|
242
|
+
affiliateFee: new CryptoAmount(baseAmount(0), destinationAsset),
|
|
243
|
+
outboundFee: new CryptoAmount(baseAmount(0), destinationAsset),
|
|
244
|
+
},
|
|
245
|
+
outboundDelayBlocks: 0,
|
|
246
|
+
outboundDelaySeconds: 0,
|
|
247
|
+
inboundConfirmationSeconds: 0,
|
|
248
|
+
inboundConfirmationBlocks: 0,
|
|
249
|
+
canSwap: false,
|
|
250
|
+
errors,
|
|
251
|
+
slipBasisPoints: 0,
|
|
252
|
+
totalSwapSeconds: 0,
|
|
253
|
+
warning: '',
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
return this.mayachainQuery.quoteSwap({
|
|
257
|
+
fromAsset,
|
|
258
|
+
fromAddress,
|
|
259
|
+
amount,
|
|
260
|
+
destinationAsset,
|
|
261
|
+
destinationAddress,
|
|
262
|
+
affiliateAddress,
|
|
263
|
+
affiliateBps,
|
|
264
|
+
toleranceBps,
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Validate swap params
|
|
270
|
+
* @param {QuoteSwapParams} quoteSwapParams Swap params
|
|
271
|
+
* @returns {string[]} the reasons the swap can not be done. If it is empty there are no reason to avoid the swap
|
|
272
|
+
*/
|
|
273
|
+
validateSwap({ fromAsset, fromAddress, destinationAsset, destinationAddress, amount, affiliateAddress, affiliateBps, }) {
|
|
274
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
275
|
+
const errors = [];
|
|
276
|
+
if (destinationAddress && !this.wallet.validateAddress(destinationAsset.chain, destinationAddress)) {
|
|
277
|
+
errors.push(`destinationAddress ${destinationAddress} is not a valid address`);
|
|
278
|
+
}
|
|
279
|
+
if (affiliateAddress) {
|
|
280
|
+
const isMayaAddress = this.wallet.validateAddress(MAYAChain, affiliateAddress);
|
|
281
|
+
const isMayaName = yield this.isMAYAName(affiliateAddress);
|
|
282
|
+
if (!(isMayaAddress || isMayaName))
|
|
283
|
+
errors.push(`affiliateAddress ${affiliateAddress} is not a valid MAYA address`);
|
|
284
|
+
}
|
|
285
|
+
if (affiliateBps && (affiliateBps < 0 || affiliateBps > 10000)) {
|
|
286
|
+
errors.push(`affiliateBps ${affiliateBps} out of range [0 - 10000]`);
|
|
287
|
+
}
|
|
288
|
+
if (this.wallet.isERC20Asset(fromAsset) && fromAddress) {
|
|
289
|
+
const inboundDetails = yield this.mayachainQuery.getChainInboundDetails(fromAsset.chain);
|
|
290
|
+
if (!inboundDetails.router)
|
|
291
|
+
throw Error(`Unknown router address for ${fromAsset.chain}`);
|
|
292
|
+
const isApprovedResult = yield this.wallet.isApproved(fromAsset, amount.baseAmount, fromAddress, inboundDetails.router);
|
|
293
|
+
if (!isApprovedResult)
|
|
294
|
+
errors.push('Maya router has not been approved to spend this amount');
|
|
295
|
+
}
|
|
296
|
+
return errors;
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Do swap between assets
|
|
301
|
+
* @param {QuoteSwapParams} quoteSwapParams Swap params
|
|
302
|
+
* @returns {TxSubmitted} the swap transaction hash and url
|
|
303
|
+
*/
|
|
304
|
+
doSwap({ fromAsset, fromAddress, amount, destinationAsset, destinationAddress, affiliateAddress, affiliateBps, toleranceBps, }) {
|
|
305
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
306
|
+
const quoteSwap = yield this.estimateSwap({
|
|
307
|
+
fromAsset,
|
|
308
|
+
fromAddress,
|
|
309
|
+
amount,
|
|
310
|
+
destinationAsset,
|
|
311
|
+
destinationAddress,
|
|
312
|
+
affiliateAddress,
|
|
313
|
+
affiliateBps,
|
|
314
|
+
toleranceBps,
|
|
315
|
+
});
|
|
316
|
+
if (!quoteSwap.canSwap)
|
|
317
|
+
throw Error(`Can not swap. ${quoteSwap.errors.join(' ')}`);
|
|
318
|
+
return fromAsset.chain === MAYAChain || fromAsset.synth
|
|
319
|
+
? this.doProtocolAssetSwap(amount, quoteSwap.memo)
|
|
320
|
+
: this.doNonProtocolAssetSwap(amount, quoteSwap.toAddress, quoteSwap.memo);
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Check if a name is a valid MAYAName
|
|
325
|
+
* @param {string} name MAYAName
|
|
326
|
+
* @returns {boolean} true if is a registered MAYAName, otherwise, false
|
|
327
|
+
*/
|
|
328
|
+
isMAYAName(name) {
|
|
329
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
330
|
+
return !!(yield this.mayachainQuery.getMAYANameDetails(name));
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Do swap from native protocol asset to any other asset
|
|
335
|
+
* @param {CryptoAmount} amount Amount to swap
|
|
336
|
+
* @param {string} memo Memo to add to the transaction to successfully make the swap
|
|
337
|
+
* @returns {TxSubmitted} the swap transaction hash and url
|
|
338
|
+
*/
|
|
339
|
+
doProtocolAssetSwap(amount, memo) {
|
|
340
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
341
|
+
const hash = yield this.wallet.deposit({ asset: amount.asset, amount: amount.baseAmount, memo });
|
|
342
|
+
return {
|
|
343
|
+
hash,
|
|
344
|
+
url: yield this.wallet.getExplorerTxUrl(amount.asset.chain, hash),
|
|
345
|
+
};
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Do swap between assets
|
|
350
|
+
* @param {CryptoAmount} amount Amount to swap
|
|
351
|
+
* @param {string} memo Memo to add to the transaction to successfully make the swap
|
|
352
|
+
* @param {string} recipient inbound address to make swap transaction to
|
|
353
|
+
* @returns {TxSubmitted} the swap transaction hash and url
|
|
354
|
+
*/
|
|
355
|
+
doNonProtocolAssetSwap(amount, recipient, memo) {
|
|
356
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
357
|
+
// Non ERC20 swaps
|
|
358
|
+
if (!this.wallet.isERC20Asset(amount.asset)) {
|
|
359
|
+
const hash = yield this.wallet.transfer({
|
|
360
|
+
asset: amount.asset,
|
|
361
|
+
amount: amount.baseAmount,
|
|
362
|
+
recipient,
|
|
363
|
+
memo,
|
|
364
|
+
});
|
|
365
|
+
return {
|
|
366
|
+
hash,
|
|
367
|
+
url: yield this.wallet.getExplorerTxUrl(amount.asset.chain, hash),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
// ERC20 swaps
|
|
371
|
+
const inboundDetails = yield this.mayachainQuery.getChainInboundDetails(amount.asset.chain);
|
|
372
|
+
if (!inboundDetails.router)
|
|
373
|
+
throw Error(`Unknown router for ${amount.asset.chain} chain`);
|
|
374
|
+
const contractAddress = getContractAddressFromAsset(amount.asset);
|
|
375
|
+
const checkSummedContractAddress = ethers.utils.getAddress(contractAddress);
|
|
376
|
+
const expiration = Math.floor(new Date(new Date().getTime() + 15 * 60000).getTime() / 1000);
|
|
377
|
+
const depositParams = [
|
|
378
|
+
recipient,
|
|
379
|
+
checkSummedContractAddress,
|
|
380
|
+
amount.baseAmount.amount().toFixed(),
|
|
381
|
+
memo,
|
|
382
|
+
expiration,
|
|
383
|
+
];
|
|
384
|
+
const routerContract = new ethers.Contract(inboundDetails.router, abi.router);
|
|
385
|
+
const wallet = this.wallet.getChainWallet(amount.asset.chain);
|
|
386
|
+
const gasPrices = yield this.wallet.getGasFeeRates(amount.asset.chain);
|
|
387
|
+
const unsignedTx = yield routerContract.populateTransaction.depositWithExpiry(...depositParams, {
|
|
388
|
+
from: this.wallet.getAddress(amount.asset.chain),
|
|
389
|
+
value: 0,
|
|
390
|
+
gasPrice: gasPrices.fast.amount().toFixed(),
|
|
391
|
+
gasLimit: '160000',
|
|
392
|
+
});
|
|
393
|
+
const { hash } = yield wallet.sendTransaction(unsignedTx);
|
|
394
|
+
return {
|
|
395
|
+
hash,
|
|
396
|
+
url: yield this.wallet.getExplorerTxUrl(amount.asset.chain, hash),
|
|
397
|
+
};
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export { MayachainAMM, Wallet };
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var xchainBitcoin = require('@xchainjs/xchain-bitcoin');
|
|
6
|
+
var xchainDash = require('@xchainjs/xchain-dash');
|
|
7
|
+
var xchainEthereum = require('@xchainjs/xchain-ethereum');
|
|
8
|
+
var xchainEvm = require('@xchainjs/xchain-evm');
|
|
9
|
+
var xchainKujira = require('@xchainjs/xchain-kujira');
|
|
10
|
+
var xchainMayachain = require('@xchainjs/xchain-mayachain');
|
|
11
|
+
var xchainThorchain = require('@xchainjs/xchain-thorchain');
|
|
12
|
+
var xchainUtil = require('@xchainjs/xchain-util');
|
|
13
|
+
var xchainClient = require('@xchainjs/xchain-client');
|
|
14
|
+
var xchainMayachainQuery = require('@xchainjs/xchain-mayachain-query');
|
|
15
|
+
var ethers = require('ethers');
|
|
16
|
+
|
|
17
|
+
/******************************************************************************
|
|
18
|
+
Copyright (c) Microsoft Corporation.
|
|
19
|
+
|
|
20
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
21
|
+
purpose with or without fee is hereby granted.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
24
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
25
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
26
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
27
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
28
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
29
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
30
|
+
***************************************************************************** */
|
|
31
|
+
|
|
32
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
33
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
34
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
35
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
36
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
37
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
38
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class Wallet {
|
|
43
|
+
/**
|
|
44
|
+
* Constructor to create a Wallet
|
|
45
|
+
*
|
|
46
|
+
* @param {string} phrase - mnemonic phrase
|
|
47
|
+
* @param {Network} network - mnemonic phrase
|
|
48
|
+
* @param {ChainConfigs} chainConfigs - Config by chain. If it isn not set, each client uses its default params
|
|
49
|
+
* @returns Wallet
|
|
50
|
+
*/
|
|
51
|
+
constructor(phrase, network, chainConfigs = {}) {
|
|
52
|
+
const settings = { network, phrase };
|
|
53
|
+
this.clients = {
|
|
54
|
+
BTC: new xchainBitcoin.Client(Object.assign(Object.assign(Object.assign({}, xchainBitcoin.defaultBTCParams), chainConfigs[xchainBitcoin.BTCChain]), settings)),
|
|
55
|
+
ETH: new xchainEthereum.Client(Object.assign(Object.assign(Object.assign({}, xchainEthereum.defaultEthParams), chainConfigs[xchainEthereum.ETHChain]), settings)),
|
|
56
|
+
DASH: new xchainDash.Client(Object.assign(Object.assign(Object.assign({}, xchainDash.defaultDashParams), chainConfigs[xchainDash.DASHChain]), settings)),
|
|
57
|
+
KUJI: new xchainKujira.Client(Object.assign(Object.assign(Object.assign({}, xchainKujira.defaultKujiParams), chainConfigs[xchainKujira.KUJIChain]), settings)),
|
|
58
|
+
THOR: new xchainThorchain.Client(Object.assign(Object.assign({}, chainConfigs[xchainThorchain.THORChain]), settings)),
|
|
59
|
+
MAYA: new xchainMayachain.Client(Object.assign(Object.assign({}, chainConfigs[xchainMayachain.MAYAChain]), settings)),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get chain address
|
|
64
|
+
* @param {Chain} chain
|
|
65
|
+
* @returns the chain address
|
|
66
|
+
*/
|
|
67
|
+
getAddress(chain) {
|
|
68
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
69
|
+
const client = this.getChainClient(chain);
|
|
70
|
+
return client.getAddressAsync();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check if address is valid
|
|
75
|
+
* @param {Chain} chain in which the address has to be valid
|
|
76
|
+
* @param {string} address to validate
|
|
77
|
+
* @returns true if it is a valid address, otherwise, false
|
|
78
|
+
*/
|
|
79
|
+
validateAddress(chain, address) {
|
|
80
|
+
const client = this.getChainClient(chain);
|
|
81
|
+
return client.validateAddress(address);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Make a transaction
|
|
85
|
+
* @param {TxParams} txParams to make the transfer
|
|
86
|
+
* @returns the transaction hash
|
|
87
|
+
*/
|
|
88
|
+
transfer({ asset, amount, recipient, memo, walletIndex }) {
|
|
89
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
90
|
+
const client = this.getChainClient(asset.chain);
|
|
91
|
+
return client.transfer({
|
|
92
|
+
walletIndex,
|
|
93
|
+
asset,
|
|
94
|
+
amount,
|
|
95
|
+
recipient,
|
|
96
|
+
memo,
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Make a deposit
|
|
102
|
+
* @param {DepositParam} depositParams
|
|
103
|
+
* @returns the hash of the deposit
|
|
104
|
+
* @throws {Error} if can not make deposit with the asset
|
|
105
|
+
*/
|
|
106
|
+
deposit({ asset, amount, memo }) {
|
|
107
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
108
|
+
const client = this.getChainClient(asset.chain);
|
|
109
|
+
if (!('deposit' in client))
|
|
110
|
+
throw Error(`Can not deposit with ${asset.chain} client`);
|
|
111
|
+
return client.deposit({ asset, amount, memo });
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Check if an spenderAddress is allowed to spend in name of another address certain asset amount
|
|
116
|
+
* @param {Asset} asset to check
|
|
117
|
+
* @param {BaseAmount} amount to check
|
|
118
|
+
* @param {string} fromAddress owner of the amount asset
|
|
119
|
+
* @param {string} spenderAddress spender to check if it is allowed to spend
|
|
120
|
+
* @returns true if the spenderAddress is allowed to spend the amount, otherwise, false
|
|
121
|
+
* @throws {Error} if asset is a non ERC20 asset
|
|
122
|
+
*/
|
|
123
|
+
isApproved(asset, amount, fromAddress, spenderAddress) {
|
|
124
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
125
|
+
const client = this.getChainClient(asset.chain);
|
|
126
|
+
if (!this.isERC20Asset(asset))
|
|
127
|
+
throw Error(`${xchainUtil.assetToString(asset)} is not an ERC20 token`);
|
|
128
|
+
const contractAddress = xchainUtil.getContractAddressFromAsset(asset);
|
|
129
|
+
return xchainEvm.isApproved({
|
|
130
|
+
provider: client.getProvider(),
|
|
131
|
+
amount,
|
|
132
|
+
spenderAddress,
|
|
133
|
+
contractAddress,
|
|
134
|
+
fromAddress,
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get transaction url
|
|
140
|
+
* @param {Chain} chain of the transaction
|
|
141
|
+
* @param {string} hash of the transaction
|
|
142
|
+
* @returns the transaction url
|
|
143
|
+
*/
|
|
144
|
+
getExplorerTxUrl(chain, hash) {
|
|
145
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
146
|
+
const client = this.getChainClient(chain);
|
|
147
|
+
return client.getExplorerTxUrl(hash);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get feeRates
|
|
152
|
+
* @param {Chain} chain of which return the feeRates
|
|
153
|
+
* @returns the gas fee rates
|
|
154
|
+
* @throws {Error} if gas fee rates can not be returned from the chain
|
|
155
|
+
*/
|
|
156
|
+
getGasFeeRates(chain) {
|
|
157
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
158
|
+
const client = this.getChainClient(chain);
|
|
159
|
+
if (!('estimateGasPrices' in client))
|
|
160
|
+
throw Error(`Can not get gas with ${chain} client`);
|
|
161
|
+
return client.estimateGasPrices();
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get chain wallet
|
|
166
|
+
* @param {Chain} chain of which return the wallet
|
|
167
|
+
* @returns the chain wallet
|
|
168
|
+
* @throws {Error} wallet can not be retrieve from chain
|
|
169
|
+
*/
|
|
170
|
+
getChainWallet(chain) {
|
|
171
|
+
const client = this.getChainClient(chain);
|
|
172
|
+
if (!('getWallet' in client))
|
|
173
|
+
throw Error(`Can not get wallet of ${chain} client`);
|
|
174
|
+
return client.getWallet();
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get chain client
|
|
178
|
+
* @param {Chain} chain of which return the client
|
|
179
|
+
* @returns the chain client
|
|
180
|
+
* @throws {Error} if client does not exist
|
|
181
|
+
*/
|
|
182
|
+
getChainClient(chain) {
|
|
183
|
+
const client = this.clients[chain];
|
|
184
|
+
if (!client)
|
|
185
|
+
throw Error(`Client not found for ${chain} chain`);
|
|
186
|
+
return client;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Check if asset is ERC20
|
|
190
|
+
* @param {Asset} asset to check
|
|
191
|
+
* @returns true if asset is ERC20, otherwise, false
|
|
192
|
+
*/
|
|
193
|
+
isERC20Asset(asset) {
|
|
194
|
+
const isGasAsset = [xchainEthereum.AssetETH.symbol].includes(asset.symbol);
|
|
195
|
+
return this.isEVMChain(asset.chain) && !isGasAsset;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Check if asset chain is EVM
|
|
199
|
+
* @param {Chain} chain to check
|
|
200
|
+
* @returns true if chain is EVM, otherwise, false
|
|
201
|
+
*/
|
|
202
|
+
isEVMChain(chain) {
|
|
203
|
+
return [xchainEthereum.AssetETH.chain].includes(chain);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* MAYAChainAMM class for interacting with THORChain.
|
|
209
|
+
* Recommended main class to use for swapping with MAYAChain
|
|
210
|
+
* Has access to Midgard and MayaNode data
|
|
211
|
+
*/
|
|
212
|
+
class MayachainAMM {
|
|
213
|
+
/**
|
|
214
|
+
* Constructor to create a MayachainAMM
|
|
215
|
+
* @param mayachainQuery - an instance of the MayachainQuery
|
|
216
|
+
* @returns MayachainAMM
|
|
217
|
+
*/
|
|
218
|
+
constructor(mayachainQuery = new xchainMayachainQuery.MayachainQuery(), wallet = new Wallet('', xchainClient.Network.Mainnet)) {
|
|
219
|
+
this.mayachainQuery = mayachainQuery;
|
|
220
|
+
this.wallet = wallet;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Estimate swap validating the swap params
|
|
224
|
+
* @param {QuoteSwapParams} quoteSwapParams Swap params
|
|
225
|
+
* @returns {QuoteSwap} Quote swap. If swap can not be done, it returns an empty QuoteSwap with the reasons the swap can not be done
|
|
226
|
+
*/
|
|
227
|
+
estimateSwap({ fromAsset, fromAddress, amount, destinationAsset, destinationAddress, affiliateAddress, affiliateBps, toleranceBps, }) {
|
|
228
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
229
|
+
const errors = yield this.validateSwap({
|
|
230
|
+
fromAsset,
|
|
231
|
+
fromAddress,
|
|
232
|
+
amount,
|
|
233
|
+
destinationAsset,
|
|
234
|
+
destinationAddress,
|
|
235
|
+
affiliateAddress,
|
|
236
|
+
affiliateBps,
|
|
237
|
+
});
|
|
238
|
+
if (errors.length > 0) {
|
|
239
|
+
return {
|
|
240
|
+
toAddress: ``,
|
|
241
|
+
memo: ``,
|
|
242
|
+
expectedAmount: new xchainUtil.CryptoAmount(xchainUtil.baseAmount(0), destinationAsset),
|
|
243
|
+
dustThreshold: this.mayachainQuery.getChainDustValue(fromAsset.chain),
|
|
244
|
+
fees: {
|
|
245
|
+
asset: destinationAsset,
|
|
246
|
+
affiliateFee: new xchainUtil.CryptoAmount(xchainUtil.baseAmount(0), destinationAsset),
|
|
247
|
+
outboundFee: new xchainUtil.CryptoAmount(xchainUtil.baseAmount(0), destinationAsset),
|
|
248
|
+
},
|
|
249
|
+
outboundDelayBlocks: 0,
|
|
250
|
+
outboundDelaySeconds: 0,
|
|
251
|
+
inboundConfirmationSeconds: 0,
|
|
252
|
+
inboundConfirmationBlocks: 0,
|
|
253
|
+
canSwap: false,
|
|
254
|
+
errors,
|
|
255
|
+
slipBasisPoints: 0,
|
|
256
|
+
totalSwapSeconds: 0,
|
|
257
|
+
warning: '',
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
return this.mayachainQuery.quoteSwap({
|
|
261
|
+
fromAsset,
|
|
262
|
+
fromAddress,
|
|
263
|
+
amount,
|
|
264
|
+
destinationAsset,
|
|
265
|
+
destinationAddress,
|
|
266
|
+
affiliateAddress,
|
|
267
|
+
affiliateBps,
|
|
268
|
+
toleranceBps,
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Validate swap params
|
|
274
|
+
* @param {QuoteSwapParams} quoteSwapParams Swap params
|
|
275
|
+
* @returns {string[]} the reasons the swap can not be done. If it is empty there are no reason to avoid the swap
|
|
276
|
+
*/
|
|
277
|
+
validateSwap({ fromAsset, fromAddress, destinationAsset, destinationAddress, amount, affiliateAddress, affiliateBps, }) {
|
|
278
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
279
|
+
const errors = [];
|
|
280
|
+
if (destinationAddress && !this.wallet.validateAddress(destinationAsset.chain, destinationAddress)) {
|
|
281
|
+
errors.push(`destinationAddress ${destinationAddress} is not a valid address`);
|
|
282
|
+
}
|
|
283
|
+
if (affiliateAddress) {
|
|
284
|
+
const isMayaAddress = this.wallet.validateAddress(xchainMayachain.MAYAChain, affiliateAddress);
|
|
285
|
+
const isMayaName = yield this.isMAYAName(affiliateAddress);
|
|
286
|
+
if (!(isMayaAddress || isMayaName))
|
|
287
|
+
errors.push(`affiliateAddress ${affiliateAddress} is not a valid MAYA address`);
|
|
288
|
+
}
|
|
289
|
+
if (affiliateBps && (affiliateBps < 0 || affiliateBps > 10000)) {
|
|
290
|
+
errors.push(`affiliateBps ${affiliateBps} out of range [0 - 10000]`);
|
|
291
|
+
}
|
|
292
|
+
if (this.wallet.isERC20Asset(fromAsset) && fromAddress) {
|
|
293
|
+
const inboundDetails = yield this.mayachainQuery.getChainInboundDetails(fromAsset.chain);
|
|
294
|
+
if (!inboundDetails.router)
|
|
295
|
+
throw Error(`Unknown router address for ${fromAsset.chain}`);
|
|
296
|
+
const isApprovedResult = yield this.wallet.isApproved(fromAsset, amount.baseAmount, fromAddress, inboundDetails.router);
|
|
297
|
+
if (!isApprovedResult)
|
|
298
|
+
errors.push('Maya router has not been approved to spend this amount');
|
|
299
|
+
}
|
|
300
|
+
return errors;
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Do swap between assets
|
|
305
|
+
* @param {QuoteSwapParams} quoteSwapParams Swap params
|
|
306
|
+
* @returns {TxSubmitted} the swap transaction hash and url
|
|
307
|
+
*/
|
|
308
|
+
doSwap({ fromAsset, fromAddress, amount, destinationAsset, destinationAddress, affiliateAddress, affiliateBps, toleranceBps, }) {
|
|
309
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
310
|
+
const quoteSwap = yield this.estimateSwap({
|
|
311
|
+
fromAsset,
|
|
312
|
+
fromAddress,
|
|
313
|
+
amount,
|
|
314
|
+
destinationAsset,
|
|
315
|
+
destinationAddress,
|
|
316
|
+
affiliateAddress,
|
|
317
|
+
affiliateBps,
|
|
318
|
+
toleranceBps,
|
|
319
|
+
});
|
|
320
|
+
if (!quoteSwap.canSwap)
|
|
321
|
+
throw Error(`Can not swap. ${quoteSwap.errors.join(' ')}`);
|
|
322
|
+
return fromAsset.chain === xchainMayachain.MAYAChain || fromAsset.synth
|
|
323
|
+
? this.doProtocolAssetSwap(amount, quoteSwap.memo)
|
|
324
|
+
: this.doNonProtocolAssetSwap(amount, quoteSwap.toAddress, quoteSwap.memo);
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Check if a name is a valid MAYAName
|
|
329
|
+
* @param {string} name MAYAName
|
|
330
|
+
* @returns {boolean} true if is a registered MAYAName, otherwise, false
|
|
331
|
+
*/
|
|
332
|
+
isMAYAName(name) {
|
|
333
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
334
|
+
return !!(yield this.mayachainQuery.getMAYANameDetails(name));
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Do swap from native protocol asset to any other asset
|
|
339
|
+
* @param {CryptoAmount} amount Amount to swap
|
|
340
|
+
* @param {string} memo Memo to add to the transaction to successfully make the swap
|
|
341
|
+
* @returns {TxSubmitted} the swap transaction hash and url
|
|
342
|
+
*/
|
|
343
|
+
doProtocolAssetSwap(amount, memo) {
|
|
344
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
345
|
+
const hash = yield this.wallet.deposit({ asset: amount.asset, amount: amount.baseAmount, memo });
|
|
346
|
+
return {
|
|
347
|
+
hash,
|
|
348
|
+
url: yield this.wallet.getExplorerTxUrl(amount.asset.chain, hash),
|
|
349
|
+
};
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Do swap between assets
|
|
354
|
+
* @param {CryptoAmount} amount Amount to swap
|
|
355
|
+
* @param {string} memo Memo to add to the transaction to successfully make the swap
|
|
356
|
+
* @param {string} recipient inbound address to make swap transaction to
|
|
357
|
+
* @returns {TxSubmitted} the swap transaction hash and url
|
|
358
|
+
*/
|
|
359
|
+
doNonProtocolAssetSwap(amount, recipient, memo) {
|
|
360
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
361
|
+
// Non ERC20 swaps
|
|
362
|
+
if (!this.wallet.isERC20Asset(amount.asset)) {
|
|
363
|
+
const hash = yield this.wallet.transfer({
|
|
364
|
+
asset: amount.asset,
|
|
365
|
+
amount: amount.baseAmount,
|
|
366
|
+
recipient,
|
|
367
|
+
memo,
|
|
368
|
+
});
|
|
369
|
+
return {
|
|
370
|
+
hash,
|
|
371
|
+
url: yield this.wallet.getExplorerTxUrl(amount.asset.chain, hash),
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
// ERC20 swaps
|
|
375
|
+
const inboundDetails = yield this.mayachainQuery.getChainInboundDetails(amount.asset.chain);
|
|
376
|
+
if (!inboundDetails.router)
|
|
377
|
+
throw Error(`Unknown router for ${amount.asset.chain} chain`);
|
|
378
|
+
const contractAddress = xchainUtil.getContractAddressFromAsset(amount.asset);
|
|
379
|
+
const checkSummedContractAddress = ethers.ethers.utils.getAddress(contractAddress);
|
|
380
|
+
const expiration = Math.floor(new Date(new Date().getTime() + 15 * 60000).getTime() / 1000);
|
|
381
|
+
const depositParams = [
|
|
382
|
+
recipient,
|
|
383
|
+
checkSummedContractAddress,
|
|
384
|
+
amount.baseAmount.amount().toFixed(),
|
|
385
|
+
memo,
|
|
386
|
+
expiration,
|
|
387
|
+
];
|
|
388
|
+
const routerContract = new ethers.ethers.Contract(inboundDetails.router, xchainEvm.abi.router);
|
|
389
|
+
const wallet = this.wallet.getChainWallet(amount.asset.chain);
|
|
390
|
+
const gasPrices = yield this.wallet.getGasFeeRates(amount.asset.chain);
|
|
391
|
+
const unsignedTx = yield routerContract.populateTransaction.depositWithExpiry(...depositParams, {
|
|
392
|
+
from: this.wallet.getAddress(amount.asset.chain),
|
|
393
|
+
value: 0,
|
|
394
|
+
gasPrice: gasPrices.fast.amount().toFixed(),
|
|
395
|
+
gasLimit: '160000',
|
|
396
|
+
});
|
|
397
|
+
const { hash } = yield wallet.sendTransaction(unsignedTx);
|
|
398
|
+
return {
|
|
399
|
+
hash,
|
|
400
|
+
url: yield this.wallet.getExplorerTxUrl(amount.asset.chain, hash),
|
|
401
|
+
};
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
exports.MayachainAMM = MayachainAMM;
|
|
407
|
+
exports.Wallet = Wallet;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { MayachainQuery, QuoteSwap, QuoteSwapParams } from '@xchainjs/xchain-mayachain-query';
|
|
2
|
+
import { TxSubmitted } from './types';
|
|
3
|
+
import { Wallet } from './wallet';
|
|
4
|
+
/**
|
|
5
|
+
* MAYAChainAMM class for interacting with THORChain.
|
|
6
|
+
* Recommended main class to use for swapping with MAYAChain
|
|
7
|
+
* Has access to Midgard and MayaNode data
|
|
8
|
+
*/
|
|
9
|
+
export declare class MayachainAMM {
|
|
10
|
+
private mayachainQuery;
|
|
11
|
+
private wallet;
|
|
12
|
+
/**
|
|
13
|
+
* Constructor to create a MayachainAMM
|
|
14
|
+
* @param mayachainQuery - an instance of the MayachainQuery
|
|
15
|
+
* @returns MayachainAMM
|
|
16
|
+
*/
|
|
17
|
+
constructor(mayachainQuery?: MayachainQuery, wallet?: Wallet);
|
|
18
|
+
/**
|
|
19
|
+
* Estimate swap validating the swap params
|
|
20
|
+
* @param {QuoteSwapParams} quoteSwapParams Swap params
|
|
21
|
+
* @returns {QuoteSwap} Quote swap. If swap can not be done, it returns an empty QuoteSwap with the reasons the swap can not be done
|
|
22
|
+
*/
|
|
23
|
+
estimateSwap({ fromAsset, fromAddress, amount, destinationAsset, destinationAddress, affiliateAddress, affiliateBps, toleranceBps, }: QuoteSwapParams): Promise<QuoteSwap>;
|
|
24
|
+
/**
|
|
25
|
+
* Validate swap params
|
|
26
|
+
* @param {QuoteSwapParams} quoteSwapParams Swap params
|
|
27
|
+
* @returns {string[]} the reasons the swap can not be done. If it is empty there are no reason to avoid the swap
|
|
28
|
+
*/
|
|
29
|
+
validateSwap({ fromAsset, fromAddress, destinationAsset, destinationAddress, amount, affiliateAddress, affiliateBps, }: QuoteSwapParams): Promise<string[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Do swap between assets
|
|
32
|
+
* @param {QuoteSwapParams} quoteSwapParams Swap params
|
|
33
|
+
* @returns {TxSubmitted} the swap transaction hash and url
|
|
34
|
+
*/
|
|
35
|
+
doSwap({ fromAsset, fromAddress, amount, destinationAsset, destinationAddress, affiliateAddress, affiliateBps, toleranceBps, }: QuoteSwapParams): Promise<TxSubmitted>;
|
|
36
|
+
/**
|
|
37
|
+
* Check if a name is a valid MAYAName
|
|
38
|
+
* @param {string} name MAYAName
|
|
39
|
+
* @returns {boolean} true if is a registered MAYAName, otherwise, false
|
|
40
|
+
*/
|
|
41
|
+
private isMAYAName;
|
|
42
|
+
/**
|
|
43
|
+
* Do swap from native protocol asset to any other asset
|
|
44
|
+
* @param {CryptoAmount} amount Amount to swap
|
|
45
|
+
* @param {string} memo Memo to add to the transaction to successfully make the swap
|
|
46
|
+
* @returns {TxSubmitted} the swap transaction hash and url
|
|
47
|
+
*/
|
|
48
|
+
private doProtocolAssetSwap;
|
|
49
|
+
/**
|
|
50
|
+
* Do swap between assets
|
|
51
|
+
* @param {CryptoAmount} amount Amount to swap
|
|
52
|
+
* @param {string} memo Memo to add to the transaction to successfully make the swap
|
|
53
|
+
* @param {string} recipient inbound address to make swap transaction to
|
|
54
|
+
* @returns {TxSubmitted} the swap transaction hash and url
|
|
55
|
+
*/
|
|
56
|
+
private doNonProtocolAssetSwap;
|
|
57
|
+
}
|
package/lib/types.d.ts
ADDED
package/lib/wallet.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { BTCChain } from '@xchainjs/xchain-bitcoin';
|
|
2
|
+
import { Network, TxParams, UtxoClientParams, XChainClient, XChainClientParams } from '@xchainjs/xchain-client';
|
|
3
|
+
import { CosmosSdkClientParams } from '@xchainjs/xchain-cosmos-sdk';
|
|
4
|
+
import { DASHChain } from '@xchainjs/xchain-dash';
|
|
5
|
+
import { ETHChain } from '@xchainjs/xchain-ethereum';
|
|
6
|
+
import { EVMClientParams, GasPrices } from '@xchainjs/xchain-evm';
|
|
7
|
+
import { KUJIChain } from '@xchainjs/xchain-kujira';
|
|
8
|
+
import { DepositParam, MAYAChain, MayachainClientParams } from '@xchainjs/xchain-mayachain';
|
|
9
|
+
import { THORChain, ThorchainClientParams } from '@xchainjs/xchain-thorchain';
|
|
10
|
+
import { Asset, BaseAmount, Chain } from '@xchainjs/xchain-util';
|
|
11
|
+
import { ethers } from 'ethers';
|
|
12
|
+
export type NodeUrls = Record<Network, string>;
|
|
13
|
+
export type ChainConfigs = Partial<{
|
|
14
|
+
[BTCChain]: Partial<Omit<UtxoClientParams, 'phrase' | 'network'>>;
|
|
15
|
+
[ETHChain]: Partial<Omit<EVMClientParams, 'phrase' | 'network'>>;
|
|
16
|
+
[DASHChain]: Partial<Omit<UtxoClientParams, 'phrase' | 'network'>>;
|
|
17
|
+
[KUJIChain]: Partial<Omit<CosmosSdkClientParams, 'phrase' | 'network'>>;
|
|
18
|
+
[THORChain]: Omit<XChainClientParams & ThorchainClientParams, 'phrase' | 'network'>;
|
|
19
|
+
[MAYAChain]: Omit<XChainClientParams & MayachainClientParams, 'phrase' | 'network'>;
|
|
20
|
+
}>;
|
|
21
|
+
export declare class Wallet {
|
|
22
|
+
clients: Record<string, XChainClient>;
|
|
23
|
+
/**
|
|
24
|
+
* Constructor to create a Wallet
|
|
25
|
+
*
|
|
26
|
+
* @param {string} phrase - mnemonic phrase
|
|
27
|
+
* @param {Network} network - mnemonic phrase
|
|
28
|
+
* @param {ChainConfigs} chainConfigs - Config by chain. If it isn not set, each client uses its default params
|
|
29
|
+
* @returns Wallet
|
|
30
|
+
*/
|
|
31
|
+
constructor(phrase: string, network: Network, chainConfigs?: ChainConfigs);
|
|
32
|
+
/**
|
|
33
|
+
* Get chain address
|
|
34
|
+
* @param {Chain} chain
|
|
35
|
+
* @returns the chain address
|
|
36
|
+
*/
|
|
37
|
+
getAddress(chain: Chain): Promise<string>;
|
|
38
|
+
/**
|
|
39
|
+
* Check if address is valid
|
|
40
|
+
* @param {Chain} chain in which the address has to be valid
|
|
41
|
+
* @param {string} address to validate
|
|
42
|
+
* @returns true if it is a valid address, otherwise, false
|
|
43
|
+
*/
|
|
44
|
+
validateAddress(chain: Chain, address: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Make a transaction
|
|
47
|
+
* @param {TxParams} txParams to make the transfer
|
|
48
|
+
* @returns the transaction hash
|
|
49
|
+
*/
|
|
50
|
+
transfer({ asset, amount, recipient, memo, walletIndex }: TxParams & {
|
|
51
|
+
asset: Asset;
|
|
52
|
+
}): Promise<string>;
|
|
53
|
+
/**
|
|
54
|
+
* Make a deposit
|
|
55
|
+
* @param {DepositParam} depositParams
|
|
56
|
+
* @returns the hash of the deposit
|
|
57
|
+
* @throws {Error} if can not make deposit with the asset
|
|
58
|
+
*/
|
|
59
|
+
deposit({ asset, amount, memo }: DepositParam & {
|
|
60
|
+
asset: Asset;
|
|
61
|
+
}): Promise<string>;
|
|
62
|
+
/**
|
|
63
|
+
* Check if an spenderAddress is allowed to spend in name of another address certain asset amount
|
|
64
|
+
* @param {Asset} asset to check
|
|
65
|
+
* @param {BaseAmount} amount to check
|
|
66
|
+
* @param {string} fromAddress owner of the amount asset
|
|
67
|
+
* @param {string} spenderAddress spender to check if it is allowed to spend
|
|
68
|
+
* @returns true if the spenderAddress is allowed to spend the amount, otherwise, false
|
|
69
|
+
* @throws {Error} if asset is a non ERC20 asset
|
|
70
|
+
*/
|
|
71
|
+
isApproved(asset: Asset, amount: BaseAmount, fromAddress: string, spenderAddress: string): Promise<boolean>;
|
|
72
|
+
/**
|
|
73
|
+
* Get transaction url
|
|
74
|
+
* @param {Chain} chain of the transaction
|
|
75
|
+
* @param {string} hash of the transaction
|
|
76
|
+
* @returns the transaction url
|
|
77
|
+
*/
|
|
78
|
+
getExplorerTxUrl(chain: Chain, hash: string): Promise<string>;
|
|
79
|
+
/**
|
|
80
|
+
* Get feeRates
|
|
81
|
+
* @param {Chain} chain of which return the feeRates
|
|
82
|
+
* @returns the gas fee rates
|
|
83
|
+
* @throws {Error} if gas fee rates can not be returned from the chain
|
|
84
|
+
*/
|
|
85
|
+
getGasFeeRates(chain: Chain): Promise<GasPrices>;
|
|
86
|
+
/**
|
|
87
|
+
* Get chain wallet
|
|
88
|
+
* @param {Chain} chain of which return the wallet
|
|
89
|
+
* @returns the chain wallet
|
|
90
|
+
* @throws {Error} wallet can not be retrieve from chain
|
|
91
|
+
*/
|
|
92
|
+
getChainWallet(chain: Chain): ethers.Wallet;
|
|
93
|
+
/**
|
|
94
|
+
* Get chain client
|
|
95
|
+
* @param {Chain} chain of which return the client
|
|
96
|
+
* @returns the chain client
|
|
97
|
+
* @throws {Error} if client does not exist
|
|
98
|
+
*/
|
|
99
|
+
private getChainClient;
|
|
100
|
+
/**
|
|
101
|
+
* Check if asset is ERC20
|
|
102
|
+
* @param {Asset} asset to check
|
|
103
|
+
* @returns true if asset is ERC20, otherwise, false
|
|
104
|
+
*/
|
|
105
|
+
isERC20Asset(asset: Asset): boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Check if asset chain is EVM
|
|
108
|
+
* @param {Chain} chain to check
|
|
109
|
+
* @returns true if chain is EVM, otherwise, false
|
|
110
|
+
*/
|
|
111
|
+
isEVMChain(chain: string): boolean;
|
|
112
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xchainjs/xchain-mayachain-amm",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "module that exposes estimating & swapping cryptocurrency assets on mayachain",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"MAYAChain",
|
|
7
|
+
"XChain"
|
|
8
|
+
],
|
|
9
|
+
"author": "MAYAChain",
|
|
10
|
+
"homepage": "https://github.com/xchainjs/xchainjs-lib#readme",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"main": "lib/index.js",
|
|
13
|
+
"module": "lib/index.esm.js",
|
|
14
|
+
"typings": "lib/index.d.ts",
|
|
15
|
+
"directories": {
|
|
16
|
+
"lib": "lib",
|
|
17
|
+
"test": "__tests__"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"lib"
|
|
21
|
+
],
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git@github.com:xchainjs/xchainjs-lib.git"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"clean": "rimraf lib/**",
|
|
28
|
+
"build": "yarn clean && rollup -c",
|
|
29
|
+
"test": "jest",
|
|
30
|
+
"e2e": "jest --config jest.config.e2e.js",
|
|
31
|
+
"lint": "eslint \"{src,__tests__}/**/*.ts\" --fix --max-warnings 0",
|
|
32
|
+
"prepublishOnly": "yarn build",
|
|
33
|
+
"postversion": "git push --follow-tags"
|
|
34
|
+
},
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/xchainjs/xchainjs-lib/issues"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@cosmos-client/core": "0.46.1",
|
|
40
|
+
"@psf/bitcoincashjs-lib": "^4.0.3",
|
|
41
|
+
"@xchainjs/xchain-bitcoin": "^0.23.6",
|
|
42
|
+
"@xchainjs/xchain-client": "^0.15.4",
|
|
43
|
+
"@xchainjs/xchain-cosmos-sdk": "^0.1.3",
|
|
44
|
+
"@xchainjs/xchain-crypto": "^0.3.0",
|
|
45
|
+
"@xchainjs/xchain-dash": "^0.2.6",
|
|
46
|
+
"@xchainjs/xchain-ethereum": "^0.31.1",
|
|
47
|
+
"@xchainjs/xchain-mayanode": "^0.1.2",
|
|
48
|
+
"@xchainjs/xchain-mayachain-query": "^0.1.1",
|
|
49
|
+
"@xchainjs/xchain-evm": "^0.4.1",
|
|
50
|
+
"@xchainjs/xchain-kujira": "^0.1.3",
|
|
51
|
+
"@xchainjs/xchain-mayachain": "^0.2.10",
|
|
52
|
+
"@xchainjs/xchain-mayamidgard": "^0.1.0",
|
|
53
|
+
"@xchainjs/xchain-thorchain": "^0.28.13",
|
|
54
|
+
"@xchainjs/xchain-util": "^0.13.1",
|
|
55
|
+
"@xchainjs/xchain-utxo-providers": "^0.2.8",
|
|
56
|
+
"axios": "^1.3.6",
|
|
57
|
+
"axios-retry": "^3.2.5",
|
|
58
|
+
"bignumber.js": "^9.0.0",
|
|
59
|
+
"bitcoinjs-lib": "5.2.0",
|
|
60
|
+
"coininfo": "^5.1.0",
|
|
61
|
+
"coinselect": "^3.1.12",
|
|
62
|
+
"dotenv": "^16.0.0",
|
|
63
|
+
"ethers": "^5.6.6",
|
|
64
|
+
"wif": "^2.0.6"
|
|
65
|
+
},
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"@cosmos-client/core": "0.46.1",
|
|
68
|
+
"@psf/bitcoincashjs-lib": "^4.0.3",
|
|
69
|
+
"@xchainjs/xchain-bitcoin": "^0.23.6",
|
|
70
|
+
"@xchainjs/xchain-client": "^0.15.4",
|
|
71
|
+
"@xchainjs/xchain-cosmos-sdk": "^0.1.3",
|
|
72
|
+
"@xchainjs/xchain-crypto": "^0.3.0",
|
|
73
|
+
"@xchainjs/xchain-dash": "^0.2.6",
|
|
74
|
+
"@xchainjs/xchain-ethereum": "^0.31.1",
|
|
75
|
+
"@xchainjs/xchain-mayanode": "^0.1.2",
|
|
76
|
+
"@xchainjs/xchain-mayachain-query": "^0.1.1",
|
|
77
|
+
"@xchainjs/xchain-evm": "^0.4.1",
|
|
78
|
+
"@xchainjs/xchain-kujira": "^0.1.3",
|
|
79
|
+
"@xchainjs/xchain-mayachain": "^0.2.10",
|
|
80
|
+
"@xchainjs/xchain-mayamidgard": "^0.1.0",
|
|
81
|
+
"@xchainjs/xchain-thorchain": "^0.28.13",
|
|
82
|
+
"@xchainjs/xchain-util": "^0.13.1",
|
|
83
|
+
"@xchainjs/xchain-utxo-providers": "^0.2.8",
|
|
84
|
+
"axios": "^1.3.6",
|
|
85
|
+
"axios-retry": "^3.2.5",
|
|
86
|
+
"bignumber.js": "^9.0.0",
|
|
87
|
+
"bitcoinjs-lib": "5.2.0",
|
|
88
|
+
"coininfo": "^5.1.0",
|
|
89
|
+
"coinselect": "^3.1.12",
|
|
90
|
+
"dotenv": "^16.0.0",
|
|
91
|
+
"ethers": "^5.6.6",
|
|
92
|
+
"wif": "^2.0.6"
|
|
93
|
+
},
|
|
94
|
+
"publishConfig": {
|
|
95
|
+
"access": "public"
|
|
96
|
+
}
|
|
97
|
+
}
|