@lifi/sdk 2.1.2 → 2.2.0-beta.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/dist/allowance/checkAllowance.d.ts +2 -2
- package/dist/allowance/checkAllowance.js +8 -3
- package/dist/allowance/utils.d.ts +2 -2
- package/dist/allowance/utils.js +5 -2
- package/dist/cjs/allowance/checkAllowance.d.ts +2 -2
- package/dist/cjs/allowance/checkAllowance.js +7 -2
- package/dist/cjs/allowance/utils.d.ts +2 -2
- package/dist/cjs/allowance/utils.js +4 -1
- package/dist/cjs/execution/StatusManager.d.ts +1 -1
- package/dist/cjs/execution/StepExecutionManager.js +84 -9
- package/dist/cjs/execution/multisig.d.ts +3 -0
- package/dist/cjs/execution/multisig.js +34 -0
- package/dist/cjs/services/ConfigService.js +2 -0
- package/dist/cjs/types/internal.types.d.ts +21 -0
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/execution/StatusManager.d.ts +1 -1
- package/dist/execution/StepExecutionManager.js +85 -10
- package/dist/execution/multisig.d.ts +3 -0
- package/dist/execution/multisig.js +27 -0
- package/dist/services/ConfigService.js +2 -0
- package/dist/types/internal.types.d.ts +21 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Signer } from 'ethers';
|
|
1
|
+
import { PopulatedTransaction, Signer } from 'ethers';
|
|
2
2
|
import { StatusManager } from '../execution/StatusManager';
|
|
3
3
|
import { Chain, InternalExecutionSettings, LifiStep } from '../types';
|
|
4
|
-
export declare const checkAllowance: (signer: Signer, step: LifiStep, statusManager: StatusManager, settings: InternalExecutionSettings, chain: Chain, allowUserInteraction?: boolean) => Promise<void>;
|
|
4
|
+
export declare const checkAllowance: (signer: Signer, step: LifiStep, statusManager: StatusManager, settings: InternalExecutionSettings, chain: Chain, allowUserInteraction?: boolean, shouldBatchTransactions?: boolean) => Promise<void | PopulatedTransaction>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import BigNumber from 'bignumber.js';
|
|
2
|
-
import { constants } from 'ethers';
|
|
2
|
+
import { constants, } from 'ethers';
|
|
3
3
|
import { getApproved, setApproval } from '../allowance/utils';
|
|
4
4
|
import { getProvider } from '../utils/getProvider';
|
|
5
5
|
import { parseError } from '../utils/parseError';
|
|
6
|
-
export const checkAllowance = async (signer, step, statusManager, settings, chain, allowUserInteraction = false) => {
|
|
6
|
+
export const checkAllowance = async (signer, step, statusManager, settings, chain, allowUserInteraction = false, shouldBatchTransactions = false) => {
|
|
7
7
|
// Ask the user to set an allowance
|
|
8
8
|
let allowanceProcess = statusManager.findOrCreateProcess(step, 'TOKEN_ALLOWANCE');
|
|
9
9
|
// Check allowance
|
|
@@ -41,7 +41,12 @@ export const checkAllowance = async (signer, step, statusManager, settings, chai
|
|
|
41
41
|
const approvalAmount = settings.infiniteApproval
|
|
42
42
|
? constants.MaxUint256.toString()
|
|
43
43
|
: step.action.fromAmount;
|
|
44
|
-
|
|
44
|
+
if (shouldBatchTransactions) {
|
|
45
|
+
const populatedTransaction = await setApproval(signer, step.action.fromToken.address, step.estimate.approvalAddress, approvalAmount, true);
|
|
46
|
+
allowanceProcess = statusManager.updateProcess(step, allowanceProcess.type, 'DONE');
|
|
47
|
+
return populatedTransaction;
|
|
48
|
+
}
|
|
49
|
+
const approveTx = (await setApproval(signer, step.action.fromToken.address, step.estimate.approvalAddress, approvalAmount));
|
|
45
50
|
allowanceProcess = statusManager.updateProcess(step, allowanceProcess.type, 'PENDING', {
|
|
46
51
|
txHash: approveTx.hash,
|
|
47
52
|
txLink: chain.metamask.blockExplorerUrls[0] + 'tx/' + approveTx.hash,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { TransactionRequest } from '@ethersproject/abstract-provider';
|
|
2
2
|
import { ChainId, Token } from '@lifi/types';
|
|
3
3
|
import BigNumber from 'bignumber.js';
|
|
4
|
-
import { ContractTransaction, Signer } from 'ethers';
|
|
4
|
+
import { ContractTransaction, PopulatedTransaction, Signer } from 'ethers';
|
|
5
5
|
import { RevokeTokenData } from '../types';
|
|
6
6
|
export declare const getApproved: (signer: Signer, tokenAddress: string, contractAddress: string, transactionRequest?: TransactionRequest) => Promise<BigNumber>;
|
|
7
|
-
export declare const setApproval: (signer: Signer, tokenAddress: string, contractAddress: string, amount: string) => Promise<ContractTransaction>;
|
|
7
|
+
export declare const setApproval: (signer: Signer, tokenAddress: string, contractAddress: string, amount: string, returnPopulatedTransaction?: boolean) => Promise<ContractTransaction | PopulatedTransaction>;
|
|
8
8
|
export declare const getAllowanceViaMulticall: (signer: Signer, chainId: ChainId, tokenData: RevokeTokenData[]) => Promise<{
|
|
9
9
|
token: Token;
|
|
10
10
|
approvalAddress: string;
|
package/dist/allowance/utils.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import BigNumber from 'bignumber.js';
|
|
2
|
-
import { Contract, ethers } from 'ethers';
|
|
2
|
+
import { Contract, ethers, } from 'ethers';
|
|
3
3
|
import ChainsService from '../services/ChainsService';
|
|
4
4
|
import { ERC20_ABI } from '../types';
|
|
5
5
|
import { ServerError } from '../utils/errors';
|
|
@@ -20,8 +20,11 @@ export const getApproved = async (signer, tokenAddress, contractAddress, transac
|
|
|
20
20
|
return new BigNumber(0);
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
|
-
export const setApproval = async (signer, tokenAddress, contractAddress, amount) => {
|
|
23
|
+
export const setApproval = async (signer, tokenAddress, contractAddress, amount, returnPopulatedTransaction) => {
|
|
24
24
|
const erc20 = new Contract(tokenAddress, ERC20_ABI, signer);
|
|
25
|
+
if (returnPopulatedTransaction) {
|
|
26
|
+
return erc20.populateTransaction.approve(contractAddress, amount);
|
|
27
|
+
}
|
|
25
28
|
const transactionRequest = await erc20.populateTransaction.approve(contractAddress, amount);
|
|
26
29
|
try {
|
|
27
30
|
const estimatedGasLimit = await signer.estimateGas(transactionRequest);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Signer } from 'ethers';
|
|
1
|
+
import { PopulatedTransaction, Signer } from 'ethers';
|
|
2
2
|
import { StatusManager } from '../execution/StatusManager';
|
|
3
3
|
import { Chain, InternalExecutionSettings, LifiStep } from '../types';
|
|
4
|
-
export declare const checkAllowance: (signer: Signer, step: LifiStep, statusManager: StatusManager, settings: InternalExecutionSettings, chain: Chain, allowUserInteraction?: boolean) => Promise<void>;
|
|
4
|
+
export declare const checkAllowance: (signer: Signer, step: LifiStep, statusManager: StatusManager, settings: InternalExecutionSettings, chain: Chain, allowUserInteraction?: boolean, shouldBatchTransactions?: boolean) => Promise<void | PopulatedTransaction>;
|
|
@@ -9,7 +9,7 @@ const ethers_1 = require("ethers");
|
|
|
9
9
|
const utils_1 = require("../allowance/utils");
|
|
10
10
|
const getProvider_1 = require("../utils/getProvider");
|
|
11
11
|
const parseError_1 = require("../utils/parseError");
|
|
12
|
-
const checkAllowance = async (signer, step, statusManager, settings, chain, allowUserInteraction = false) => {
|
|
12
|
+
const checkAllowance = async (signer, step, statusManager, settings, chain, allowUserInteraction = false, shouldBatchTransactions = false) => {
|
|
13
13
|
// Ask the user to set an allowance
|
|
14
14
|
let allowanceProcess = statusManager.findOrCreateProcess(step, 'TOKEN_ALLOWANCE');
|
|
15
15
|
// Check allowance
|
|
@@ -47,7 +47,12 @@ const checkAllowance = async (signer, step, statusManager, settings, chain, allo
|
|
|
47
47
|
const approvalAmount = settings.infiniteApproval
|
|
48
48
|
? ethers_1.constants.MaxUint256.toString()
|
|
49
49
|
: step.action.fromAmount;
|
|
50
|
-
|
|
50
|
+
if (shouldBatchTransactions) {
|
|
51
|
+
const populatedTransaction = await (0, utils_1.setApproval)(signer, step.action.fromToken.address, step.estimate.approvalAddress, approvalAmount, true);
|
|
52
|
+
allowanceProcess = statusManager.updateProcess(step, allowanceProcess.type, 'DONE');
|
|
53
|
+
return populatedTransaction;
|
|
54
|
+
}
|
|
55
|
+
const approveTx = (await (0, utils_1.setApproval)(signer, step.action.fromToken.address, step.estimate.approvalAddress, approvalAmount));
|
|
51
56
|
allowanceProcess = statusManager.updateProcess(step, allowanceProcess.type, 'PENDING', {
|
|
52
57
|
txHash: approveTx.hash,
|
|
53
58
|
txLink: chain.metamask.blockExplorerUrls[0] + 'tx/' + approveTx.hash,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { TransactionRequest } from '@ethersproject/abstract-provider';
|
|
2
2
|
import { ChainId, Token } from '@lifi/types';
|
|
3
3
|
import BigNumber from 'bignumber.js';
|
|
4
|
-
import { ContractTransaction, Signer } from 'ethers';
|
|
4
|
+
import { ContractTransaction, PopulatedTransaction, Signer } from 'ethers';
|
|
5
5
|
import { RevokeTokenData } from '../types';
|
|
6
6
|
export declare const getApproved: (signer: Signer, tokenAddress: string, contractAddress: string, transactionRequest?: TransactionRequest) => Promise<BigNumber>;
|
|
7
|
-
export declare const setApproval: (signer: Signer, tokenAddress: string, contractAddress: string, amount: string) => Promise<ContractTransaction>;
|
|
7
|
+
export declare const setApproval: (signer: Signer, tokenAddress: string, contractAddress: string, amount: string, returnPopulatedTransaction?: boolean) => Promise<ContractTransaction | PopulatedTransaction>;
|
|
8
8
|
export declare const getAllowanceViaMulticall: (signer: Signer, chainId: ChainId, tokenData: RevokeTokenData[]) => Promise<{
|
|
9
9
|
token: Token;
|
|
10
10
|
approvalAddress: string;
|
|
@@ -27,8 +27,11 @@ const getApproved = async (signer, tokenAddress, contractAddress, transactionReq
|
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
exports.getApproved = getApproved;
|
|
30
|
-
const setApproval = async (signer, tokenAddress, contractAddress, amount) => {
|
|
30
|
+
const setApproval = async (signer, tokenAddress, contractAddress, amount, returnPopulatedTransaction) => {
|
|
31
31
|
const erc20 = new ethers_1.Contract(tokenAddress, types_1.ERC20_ABI, signer);
|
|
32
|
+
if (returnPopulatedTransaction) {
|
|
33
|
+
return erc20.populateTransaction.approve(contractAddress, amount);
|
|
34
|
+
}
|
|
32
35
|
const transactionRequest = await erc20.populateTransaction.approve(contractAddress, amount);
|
|
33
36
|
try {
|
|
34
37
|
const estimatedGasLimit = await signer.estimateGas(transactionRequest);
|
|
@@ -10,7 +10,7 @@ interface Receipt {
|
|
|
10
10
|
gasAmountUSD?: string;
|
|
11
11
|
}
|
|
12
12
|
type InternalUpdateRouteCallback = (route: Route) => void;
|
|
13
|
-
type OptionalParameters = Partial<Pick<Process, 'doneAt' | 'failedAt' | 'txHash' | 'txLink' | 'error' | 'substatus' | 'substatusMessage'>>;
|
|
13
|
+
type OptionalParameters = Partial<Pick<Process, 'doneAt' | 'failedAt' | 'txHash' | 'txLink' | 'error' | 'substatus' | 'substatusMessage' | 'multisigTxHash'>>;
|
|
14
14
|
/**
|
|
15
15
|
* Manages status updates of a route and provides various functions for tracking processes
|
|
16
16
|
* @param {Route} route The route this StatusManger belongs to.
|
|
@@ -16,6 +16,8 @@ const utils_1 = require("../utils/utils");
|
|
|
16
16
|
const stepComparison_1 = require("./stepComparison");
|
|
17
17
|
const switchChain_1 = require("./switchChain");
|
|
18
18
|
const utils_2 = require("./utils");
|
|
19
|
+
const ConfigService_1 = __importDefault(require("../services/ConfigService"));
|
|
20
|
+
const multisig_1 = require("./multisig");
|
|
19
21
|
class StepExecutionManager {
|
|
20
22
|
constructor() {
|
|
21
23
|
this.allowUserInteraction = true;
|
|
@@ -23,6 +25,11 @@ class StepExecutionManager {
|
|
|
23
25
|
this.allowUserInteraction = value;
|
|
24
26
|
};
|
|
25
27
|
this.execute = async ({ signer, step, statusManager, settings, }) => {
|
|
28
|
+
const config = ConfigService_1.default.getInstance().getConfig();
|
|
29
|
+
const isMultisigSigner = !!config.multisigConfig?.isMultisigSigner;
|
|
30
|
+
const multisigBatchTransactions = [];
|
|
31
|
+
const shouldBatchTransactions = config.multisigConfig?.shouldBatchTransactions &&
|
|
32
|
+
!!config.multisigConfig.sendBatchTransaction;
|
|
26
33
|
step.execution = statusManager.initExecutionObject(step);
|
|
27
34
|
const chainsService = ChainsService_1.default.getInstance();
|
|
28
35
|
const fromChain = await chainsService.getChainById(step.action.fromChainId);
|
|
@@ -32,14 +39,43 @@ class StepExecutionManager {
|
|
|
32
39
|
// STEP 1: Check allowance
|
|
33
40
|
const existingProcess = step.execution.process.find((p) => p.type === currentProcessType);
|
|
34
41
|
// Check token approval only if fromToken is not the native token => no approval needed in that case
|
|
35
|
-
|
|
36
|
-
!(0, utils_1.isZeroAddress)(step.action.fromToken.address)
|
|
37
|
-
|
|
42
|
+
const checkForAllowance = !existingProcess?.txHash &&
|
|
43
|
+
!(0, utils_1.isZeroAddress)(step.action.fromToken.address) &&
|
|
44
|
+
(shouldBatchTransactions || !isMultisigSigner);
|
|
45
|
+
if (checkForAllowance) {
|
|
46
|
+
const populatedTransaction = await (0, allowance_1.checkAllowance)(signer, step, statusManager, settings, fromChain, this.allowUserInteraction, shouldBatchTransactions);
|
|
47
|
+
if (populatedTransaction) {
|
|
48
|
+
const { to, data } = populatedTransaction;
|
|
49
|
+
if (to && data) {
|
|
50
|
+
// allowance doesn't need value
|
|
51
|
+
const cleanedPopulatedTransaction = {
|
|
52
|
+
value: ethers_1.BigNumber.from(0).toString(),
|
|
53
|
+
to,
|
|
54
|
+
data,
|
|
55
|
+
};
|
|
56
|
+
multisigBatchTransactions.push(cleanedPopulatedTransaction);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
38
59
|
}
|
|
39
60
|
// STEP 2: Get transaction
|
|
40
61
|
let process = statusManager.findOrCreateProcess(step, currentProcessType);
|
|
41
62
|
if (process.status !== 'DONE') {
|
|
63
|
+
const multisigProcess = step.execution.process.find((p) => !!p.multisigTxHash);
|
|
42
64
|
try {
|
|
65
|
+
if (isMultisigSigner && multisigProcess) {
|
|
66
|
+
if (!multisigProcess) {
|
|
67
|
+
throw new errors_1.ValidationError('Multisig process is undefined.');
|
|
68
|
+
}
|
|
69
|
+
if (!config.multisigConfig?.getMultisigTransactionDetails) {
|
|
70
|
+
throw new errors_1.ValidationError('"getMultisigTransactionDetails()" is missing in Multisig config.');
|
|
71
|
+
}
|
|
72
|
+
const multisigTxHash = multisigProcess.multisigTxHash;
|
|
73
|
+
if (!multisigTxHash) {
|
|
74
|
+
// need to check what happens in failed tx
|
|
75
|
+
throw new errors_1.ValidationError('Multisig internal transaction hash is undefined.');
|
|
76
|
+
}
|
|
77
|
+
await (0, multisig_1.updateMultisigRouteProcess)(multisigTxHash, step, statusManager, process, fromChain);
|
|
78
|
+
}
|
|
43
79
|
let transaction;
|
|
44
80
|
if (process.txHash) {
|
|
45
81
|
// Make sure that the chain is still correct
|
|
@@ -105,8 +141,52 @@ class StepExecutionManager {
|
|
|
105
141
|
catch (error) { }
|
|
106
142
|
}
|
|
107
143
|
// Submit the transaction
|
|
108
|
-
|
|
144
|
+
if (shouldBatchTransactions &&
|
|
145
|
+
config.multisigConfig?.sendBatchTransaction) {
|
|
146
|
+
const { to, data, value } = await signer.populateTransaction(transactionRequest);
|
|
147
|
+
const isValidTransaction = to && data;
|
|
148
|
+
if (isValidTransaction) {
|
|
149
|
+
const populatedTransaction = {
|
|
150
|
+
value: value?.toString() ?? ethers_1.BigNumber.from(0).toString(),
|
|
151
|
+
to,
|
|
152
|
+
data: data.toString(),
|
|
153
|
+
};
|
|
154
|
+
multisigBatchTransactions.push(populatedTransaction);
|
|
155
|
+
transaction = await config.multisigConfig?.sendBatchTransaction(multisigBatchTransactions);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
throw new errors_1.TransactionError(errors_1.LifiErrorCode.TransactionUnprepared, 'Unable to prepare transaction.');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
transaction = await signer.sendTransaction(transactionRequest);
|
|
163
|
+
}
|
|
109
164
|
// STEP 4: Wait for the transaction
|
|
165
|
+
if (isMultisigSigner) {
|
|
166
|
+
process = statusManager.updateProcess(step, process.type, 'ACTION_REQUIRED', {
|
|
167
|
+
multisigTxHash: transaction.hash,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
process = statusManager.updateProcess(step, process.type, 'PENDING', {
|
|
172
|
+
txHash: transaction.hash,
|
|
173
|
+
txLink: fromChain.metamask.blockExplorerUrls[0] +
|
|
174
|
+
'tx/' +
|
|
175
|
+
transaction.hash,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
await transaction.wait?.();
|
|
180
|
+
// if it's multisig signer and the process is in ACTION_REQUIRED
|
|
181
|
+
// then signatures are still needed
|
|
182
|
+
if (isMultisigSigner &&
|
|
183
|
+
process.status === 'ACTION_REQUIRED' &&
|
|
184
|
+
transaction.hash) {
|
|
185
|
+
// Return the execution object without updating the process
|
|
186
|
+
// The execution would progress once all multisigs signer approve
|
|
187
|
+
await (0, multisig_1.updateMultisigRouteProcess)(transaction.hash, step, statusManager, process, fromChain);
|
|
188
|
+
}
|
|
189
|
+
if (!isMultisigSigner) {
|
|
110
190
|
process = statusManager.updateProcess(step, process.type, 'PENDING', {
|
|
111
191
|
txHash: transaction.hash,
|
|
112
192
|
txLink: fromChain.metamask.blockExplorerUrls[0] +
|
|
@@ -114,11 +194,6 @@ class StepExecutionManager {
|
|
|
114
194
|
transaction.hash,
|
|
115
195
|
});
|
|
116
196
|
}
|
|
117
|
-
await transaction.wait();
|
|
118
|
-
process = statusManager.updateProcess(step, process.type, 'PENDING', {
|
|
119
|
-
txHash: transaction.hash,
|
|
120
|
-
txLink: fromChain.metamask.blockExplorerUrls[0] + 'tx/' + transaction.hash,
|
|
121
|
-
});
|
|
122
197
|
if (isBridgeExecution) {
|
|
123
198
|
process = statusManager.updateProcess(step, process.type, 'DONE');
|
|
124
199
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ExtendedChain, LifiStep, Process } from '@lifi/types';
|
|
2
|
+
import { StatusManager } from '.';
|
|
3
|
+
export declare const updateMultisigRouteProcess: (internalTxHash: string, step: LifiStep, statusManager: StatusManager, process: Process, fromChain: ExtendedChain) => Promise<void>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.updateMultisigRouteProcess = void 0;
|
|
7
|
+
const ConfigService_1 = __importDefault(require("../services/ConfigService"));
|
|
8
|
+
const errors_1 = require("../utils/errors");
|
|
9
|
+
const updateMultisigRouteProcess = async (internalTxHash, step, statusManager, process, fromChain) => {
|
|
10
|
+
const config = ConfigService_1.default.getInstance().getConfig();
|
|
11
|
+
if (!config.multisigConfig?.getMultisigTransactionDetails) {
|
|
12
|
+
throw new Error('"getMultisigTransactionDetails()" is missing in Multisig config.');
|
|
13
|
+
}
|
|
14
|
+
const updateIntermediateMultisigStatus = () => {
|
|
15
|
+
process = statusManager.updateProcess(step, process.type, 'PENDING');
|
|
16
|
+
};
|
|
17
|
+
const multisigStatusResponse = await config.multisigConfig?.getMultisigTransactionDetails(internalTxHash, fromChain.id, updateIntermediateMultisigStatus);
|
|
18
|
+
if (multisigStatusResponse.status === 'DONE') {
|
|
19
|
+
process = statusManager.updateProcess(step, process.type, 'PENDING', {
|
|
20
|
+
txHash: multisigStatusResponse.txHash,
|
|
21
|
+
multisigTxHash: undefined,
|
|
22
|
+
txLink: fromChain.metamask.blockExplorerUrls[0] +
|
|
23
|
+
'tx/' +
|
|
24
|
+
multisigStatusResponse.txHash,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (multisigStatusResponse.status === 'FAILED') {
|
|
28
|
+
throw new errors_1.TransactionError(errors_1.LifiErrorCode.TransactionFailed, 'Multisig transaction failed.');
|
|
29
|
+
}
|
|
30
|
+
if (multisigStatusResponse.status === 'CANCELLED') {
|
|
31
|
+
throw new errors_1.TransactionError(errors_1.LifiErrorCode.TransactionRejected, 'Transaction was rejected by users.');
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
exports.updateMultisigRouteProcess = updateMultisigRouteProcess;
|
|
@@ -42,6 +42,8 @@ class ConfigService {
|
|
|
42
42
|
this.config.integrator = configUpdate.integrator || this.config.integrator;
|
|
43
43
|
this.config.widgetVersion =
|
|
44
44
|
configUpdate.widgetVersion || this.config.widgetVersion;
|
|
45
|
+
this.config.multisigConfig =
|
|
46
|
+
configUpdate.multisigConfig || this.config.multisigConfig;
|
|
45
47
|
return this.config;
|
|
46
48
|
};
|
|
47
49
|
this.updateChains = (chains) => {
|
|
@@ -35,7 +35,27 @@ export type Config = {
|
|
|
35
35
|
userId?: string;
|
|
36
36
|
integrator: string;
|
|
37
37
|
widgetVersion?: string;
|
|
38
|
+
multisigConfig?: MultisigConfig;
|
|
38
39
|
};
|
|
40
|
+
export interface MultisigTxDetails {
|
|
41
|
+
status: 'DONE' | 'FAILED' | 'PENDING' | 'CANCELLED';
|
|
42
|
+
message: string;
|
|
43
|
+
txHash?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface MultisigTransactionResponse {
|
|
46
|
+
hash: string;
|
|
47
|
+
}
|
|
48
|
+
export interface BaseTransaction {
|
|
49
|
+
to: string;
|
|
50
|
+
value: string;
|
|
51
|
+
data: string;
|
|
52
|
+
}
|
|
53
|
+
export interface MultisigConfig {
|
|
54
|
+
isMultisigSigner: boolean;
|
|
55
|
+
getMultisigTransactionDetails: (txHash: string, fromChainId: number, updateIntermediateStatus?: () => void) => Promise<MultisigTxDetails>;
|
|
56
|
+
sendBatchTransaction?: (batchTransactions: BaseTransaction[]) => Promise<MultisigTransactionResponse>;
|
|
57
|
+
shouldBatchTransactions?: boolean;
|
|
58
|
+
}
|
|
39
59
|
export type ConfigUpdate = {
|
|
40
60
|
apiUrl?: string;
|
|
41
61
|
rpcs?: Record<number, string[]>;
|
|
@@ -46,6 +66,7 @@ export type ConfigUpdate = {
|
|
|
46
66
|
userId?: string;
|
|
47
67
|
integrator: string;
|
|
48
68
|
widgetVersion?: string;
|
|
69
|
+
multisigConfig?: MultisigConfig;
|
|
49
70
|
};
|
|
50
71
|
export type SwitchChainHook = (requiredChainId: number) => Promise<Signer | undefined>;
|
|
51
72
|
export interface AcceptSlippageUpdateHookParams {
|
package/dist/cjs/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const name = "@lifi/sdk";
|
|
2
|
-
export declare const version = "2.
|
|
2
|
+
export declare const version = "2.2.0-beta.0";
|
package/dist/cjs/version.js
CHANGED
|
@@ -10,7 +10,7 @@ interface Receipt {
|
|
|
10
10
|
gasAmountUSD?: string;
|
|
11
11
|
}
|
|
12
12
|
type InternalUpdateRouteCallback = (route: Route) => void;
|
|
13
|
-
type OptionalParameters = Partial<Pick<Process, 'doneAt' | 'failedAt' | 'txHash' | 'txLink' | 'error' | 'substatus' | 'substatusMessage'>>;
|
|
13
|
+
type OptionalParameters = Partial<Pick<Process, 'doneAt' | 'failedAt' | 'txHash' | 'txLink' | 'error' | 'substatus' | 'substatusMessage' | 'multisigTxHash'>>;
|
|
14
14
|
/**
|
|
15
15
|
* Manages status updates of a route and provides various functions for tracking processes
|
|
16
16
|
* @param {Route} route The route this StatusManger belongs to.
|
|
@@ -3,13 +3,15 @@ import { checkAllowance } from '../allowance';
|
|
|
3
3
|
import { checkBalance } from '../balance';
|
|
4
4
|
import ApiService from '../services/ApiService';
|
|
5
5
|
import ChainsService from '../services/ChainsService';
|
|
6
|
-
import { LifiErrorCode, TransactionError } from '../utils/errors';
|
|
6
|
+
import { LifiErrorCode, TransactionError, ValidationError, } from '../utils/errors';
|
|
7
7
|
import { getProvider } from '../utils/getProvider';
|
|
8
8
|
import { getTransactionFailedMessage, parseError } from '../utils/parseError';
|
|
9
9
|
import { isZeroAddress, personalizeStep } from '../utils/utils';
|
|
10
10
|
import { stepComparison } from './stepComparison';
|
|
11
11
|
import { switchChain } from './switchChain';
|
|
12
12
|
import { getSubstatusMessage, waitForReceivingTransaction } from './utils';
|
|
13
|
+
import ConfigService from '../services/ConfigService';
|
|
14
|
+
import { updateMultisigRouteProcess } from './multisig';
|
|
13
15
|
export class StepExecutionManager {
|
|
14
16
|
constructor() {
|
|
15
17
|
this.allowUserInteraction = true;
|
|
@@ -17,6 +19,11 @@ export class StepExecutionManager {
|
|
|
17
19
|
this.allowUserInteraction = value;
|
|
18
20
|
};
|
|
19
21
|
this.execute = async ({ signer, step, statusManager, settings, }) => {
|
|
22
|
+
const config = ConfigService.getInstance().getConfig();
|
|
23
|
+
const isMultisigSigner = !!config.multisigConfig?.isMultisigSigner;
|
|
24
|
+
const multisigBatchTransactions = [];
|
|
25
|
+
const shouldBatchTransactions = config.multisigConfig?.shouldBatchTransactions &&
|
|
26
|
+
!!config.multisigConfig.sendBatchTransaction;
|
|
20
27
|
step.execution = statusManager.initExecutionObject(step);
|
|
21
28
|
const chainsService = ChainsService.getInstance();
|
|
22
29
|
const fromChain = await chainsService.getChainById(step.action.fromChainId);
|
|
@@ -26,14 +33,43 @@ export class StepExecutionManager {
|
|
|
26
33
|
// STEP 1: Check allowance
|
|
27
34
|
const existingProcess = step.execution.process.find((p) => p.type === currentProcessType);
|
|
28
35
|
// Check token approval only if fromToken is not the native token => no approval needed in that case
|
|
29
|
-
|
|
30
|
-
!isZeroAddress(step.action.fromToken.address)
|
|
31
|
-
|
|
36
|
+
const checkForAllowance = !existingProcess?.txHash &&
|
|
37
|
+
!isZeroAddress(step.action.fromToken.address) &&
|
|
38
|
+
(shouldBatchTransactions || !isMultisigSigner);
|
|
39
|
+
if (checkForAllowance) {
|
|
40
|
+
const populatedTransaction = await checkAllowance(signer, step, statusManager, settings, fromChain, this.allowUserInteraction, shouldBatchTransactions);
|
|
41
|
+
if (populatedTransaction) {
|
|
42
|
+
const { to, data } = populatedTransaction;
|
|
43
|
+
if (to && data) {
|
|
44
|
+
// allowance doesn't need value
|
|
45
|
+
const cleanedPopulatedTransaction = {
|
|
46
|
+
value: BigNumber.from(0).toString(),
|
|
47
|
+
to,
|
|
48
|
+
data,
|
|
49
|
+
};
|
|
50
|
+
multisigBatchTransactions.push(cleanedPopulatedTransaction);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
32
53
|
}
|
|
33
54
|
// STEP 2: Get transaction
|
|
34
55
|
let process = statusManager.findOrCreateProcess(step, currentProcessType);
|
|
35
56
|
if (process.status !== 'DONE') {
|
|
57
|
+
const multisigProcess = step.execution.process.find((p) => !!p.multisigTxHash);
|
|
36
58
|
try {
|
|
59
|
+
if (isMultisigSigner && multisigProcess) {
|
|
60
|
+
if (!multisigProcess) {
|
|
61
|
+
throw new ValidationError('Multisig process is undefined.');
|
|
62
|
+
}
|
|
63
|
+
if (!config.multisigConfig?.getMultisigTransactionDetails) {
|
|
64
|
+
throw new ValidationError('"getMultisigTransactionDetails()" is missing in Multisig config.');
|
|
65
|
+
}
|
|
66
|
+
const multisigTxHash = multisigProcess.multisigTxHash;
|
|
67
|
+
if (!multisigTxHash) {
|
|
68
|
+
// need to check what happens in failed tx
|
|
69
|
+
throw new ValidationError('Multisig internal transaction hash is undefined.');
|
|
70
|
+
}
|
|
71
|
+
await updateMultisigRouteProcess(multisigTxHash, step, statusManager, process, fromChain);
|
|
72
|
+
}
|
|
37
73
|
let transaction;
|
|
38
74
|
if (process.txHash) {
|
|
39
75
|
// Make sure that the chain is still correct
|
|
@@ -99,8 +135,52 @@ export class StepExecutionManager {
|
|
|
99
135
|
catch (error) { }
|
|
100
136
|
}
|
|
101
137
|
// Submit the transaction
|
|
102
|
-
|
|
138
|
+
if (shouldBatchTransactions &&
|
|
139
|
+
config.multisigConfig?.sendBatchTransaction) {
|
|
140
|
+
const { to, data, value } = await signer.populateTransaction(transactionRequest);
|
|
141
|
+
const isValidTransaction = to && data;
|
|
142
|
+
if (isValidTransaction) {
|
|
143
|
+
const populatedTransaction = {
|
|
144
|
+
value: value?.toString() ?? BigNumber.from(0).toString(),
|
|
145
|
+
to,
|
|
146
|
+
data: data.toString(),
|
|
147
|
+
};
|
|
148
|
+
multisigBatchTransactions.push(populatedTransaction);
|
|
149
|
+
transaction = await config.multisigConfig?.sendBatchTransaction(multisigBatchTransactions);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
throw new TransactionError(LifiErrorCode.TransactionUnprepared, 'Unable to prepare transaction.');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
transaction = await signer.sendTransaction(transactionRequest);
|
|
157
|
+
}
|
|
103
158
|
// STEP 4: Wait for the transaction
|
|
159
|
+
if (isMultisigSigner) {
|
|
160
|
+
process = statusManager.updateProcess(step, process.type, 'ACTION_REQUIRED', {
|
|
161
|
+
multisigTxHash: transaction.hash,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
process = statusManager.updateProcess(step, process.type, 'PENDING', {
|
|
166
|
+
txHash: transaction.hash,
|
|
167
|
+
txLink: fromChain.metamask.blockExplorerUrls[0] +
|
|
168
|
+
'tx/' +
|
|
169
|
+
transaction.hash,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
await transaction.wait?.();
|
|
174
|
+
// if it's multisig signer and the process is in ACTION_REQUIRED
|
|
175
|
+
// then signatures are still needed
|
|
176
|
+
if (isMultisigSigner &&
|
|
177
|
+
process.status === 'ACTION_REQUIRED' &&
|
|
178
|
+
transaction.hash) {
|
|
179
|
+
// Return the execution object without updating the process
|
|
180
|
+
// The execution would progress once all multisigs signer approve
|
|
181
|
+
await updateMultisigRouteProcess(transaction.hash, step, statusManager, process, fromChain);
|
|
182
|
+
}
|
|
183
|
+
if (!isMultisigSigner) {
|
|
104
184
|
process = statusManager.updateProcess(step, process.type, 'PENDING', {
|
|
105
185
|
txHash: transaction.hash,
|
|
106
186
|
txLink: fromChain.metamask.blockExplorerUrls[0] +
|
|
@@ -108,11 +188,6 @@ export class StepExecutionManager {
|
|
|
108
188
|
transaction.hash,
|
|
109
189
|
});
|
|
110
190
|
}
|
|
111
|
-
await transaction.wait();
|
|
112
|
-
process = statusManager.updateProcess(step, process.type, 'PENDING', {
|
|
113
|
-
txHash: transaction.hash,
|
|
114
|
-
txLink: fromChain.metamask.blockExplorerUrls[0] + 'tx/' + transaction.hash,
|
|
115
|
-
});
|
|
116
191
|
if (isBridgeExecution) {
|
|
117
192
|
process = statusManager.updateProcess(step, process.type, 'DONE');
|
|
118
193
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ExtendedChain, LifiStep, Process } from '@lifi/types';
|
|
2
|
+
import { StatusManager } from '.';
|
|
3
|
+
export declare const updateMultisigRouteProcess: (internalTxHash: string, step: LifiStep, statusManager: StatusManager, process: Process, fromChain: ExtendedChain) => Promise<void>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import ConfigService from '../services/ConfigService';
|
|
2
|
+
import { LifiErrorCode, TransactionError } from '../utils/errors';
|
|
3
|
+
export const updateMultisigRouteProcess = async (internalTxHash, step, statusManager, process, fromChain) => {
|
|
4
|
+
const config = ConfigService.getInstance().getConfig();
|
|
5
|
+
if (!config.multisigConfig?.getMultisigTransactionDetails) {
|
|
6
|
+
throw new Error('"getMultisigTransactionDetails()" is missing in Multisig config.');
|
|
7
|
+
}
|
|
8
|
+
const updateIntermediateMultisigStatus = () => {
|
|
9
|
+
process = statusManager.updateProcess(step, process.type, 'PENDING');
|
|
10
|
+
};
|
|
11
|
+
const multisigStatusResponse = await config.multisigConfig?.getMultisigTransactionDetails(internalTxHash, fromChain.id, updateIntermediateMultisigStatus);
|
|
12
|
+
if (multisigStatusResponse.status === 'DONE') {
|
|
13
|
+
process = statusManager.updateProcess(step, process.type, 'PENDING', {
|
|
14
|
+
txHash: multisigStatusResponse.txHash,
|
|
15
|
+
multisigTxHash: undefined,
|
|
16
|
+
txLink: fromChain.metamask.blockExplorerUrls[0] +
|
|
17
|
+
'tx/' +
|
|
18
|
+
multisigStatusResponse.txHash,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
if (multisigStatusResponse.status === 'FAILED') {
|
|
22
|
+
throw new TransactionError(LifiErrorCode.TransactionFailed, 'Multisig transaction failed.');
|
|
23
|
+
}
|
|
24
|
+
if (multisigStatusResponse.status === 'CANCELLED') {
|
|
25
|
+
throw new TransactionError(LifiErrorCode.TransactionRejected, 'Transaction was rejected by users.');
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -40,6 +40,8 @@ class ConfigService {
|
|
|
40
40
|
this.config.integrator = configUpdate.integrator || this.config.integrator;
|
|
41
41
|
this.config.widgetVersion =
|
|
42
42
|
configUpdate.widgetVersion || this.config.widgetVersion;
|
|
43
|
+
this.config.multisigConfig =
|
|
44
|
+
configUpdate.multisigConfig || this.config.multisigConfig;
|
|
43
45
|
return this.config;
|
|
44
46
|
};
|
|
45
47
|
this.updateChains = (chains) => {
|
|
@@ -35,7 +35,27 @@ export type Config = {
|
|
|
35
35
|
userId?: string;
|
|
36
36
|
integrator: string;
|
|
37
37
|
widgetVersion?: string;
|
|
38
|
+
multisigConfig?: MultisigConfig;
|
|
38
39
|
};
|
|
40
|
+
export interface MultisigTxDetails {
|
|
41
|
+
status: 'DONE' | 'FAILED' | 'PENDING' | 'CANCELLED';
|
|
42
|
+
message: string;
|
|
43
|
+
txHash?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface MultisigTransactionResponse {
|
|
46
|
+
hash: string;
|
|
47
|
+
}
|
|
48
|
+
export interface BaseTransaction {
|
|
49
|
+
to: string;
|
|
50
|
+
value: string;
|
|
51
|
+
data: string;
|
|
52
|
+
}
|
|
53
|
+
export interface MultisigConfig {
|
|
54
|
+
isMultisigSigner: boolean;
|
|
55
|
+
getMultisigTransactionDetails: (txHash: string, fromChainId: number, updateIntermediateStatus?: () => void) => Promise<MultisigTxDetails>;
|
|
56
|
+
sendBatchTransaction?: (batchTransactions: BaseTransaction[]) => Promise<MultisigTransactionResponse>;
|
|
57
|
+
shouldBatchTransactions?: boolean;
|
|
58
|
+
}
|
|
39
59
|
export type ConfigUpdate = {
|
|
40
60
|
apiUrl?: string;
|
|
41
61
|
rpcs?: Record<number, string[]>;
|
|
@@ -46,6 +66,7 @@ export type ConfigUpdate = {
|
|
|
46
66
|
userId?: string;
|
|
47
67
|
integrator: string;
|
|
48
68
|
widgetVersion?: string;
|
|
69
|
+
multisigConfig?: MultisigConfig;
|
|
49
70
|
};
|
|
50
71
|
export type SwitchChainHook = (requiredChainId: number) => Promise<Signer | undefined>;
|
|
51
72
|
export interface AcceptSlippageUpdateHookParams {
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const name = "@lifi/sdk";
|
|
2
|
-
export declare const version = "2.
|
|
2
|
+
export declare const version = "2.2.0-beta.0";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const name = '@lifi/sdk';
|
|
2
|
-
export const version = '2.
|
|
2
|
+
export const version = '2.2.0-beta.0';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lifi/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0-beta.0",
|
|
4
4
|
"description": "LI.FI Any-to-Any Cross-Chain-Swap SDK",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"prepublishOnly": "run-s build use:npmReadme && pinst --enable",
|
|
27
27
|
"postpublish": "npm run use:gitReadme && pinst --enable",
|
|
28
28
|
"prepare": "husky install",
|
|
29
|
-
"release": "standard-version -a"
|
|
29
|
+
"release": "standard-version -a",
|
|
30
|
+
"release:beta": "standard-version -a --prerelease beta --skip.changelog"
|
|
30
31
|
},
|
|
31
32
|
"standard-version": {
|
|
32
33
|
"scripts": {
|