@lombard.finance/sdk 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/README.md +32 -0
- package/dist/index.js +12574 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
- package/src/btcSdk/utils/getOutputScript.ts +54 -0
- package/src/common/const.ts +3 -0
- package/src/common/types/internalTypes.ts +8 -0
- package/src/common/types/types.ts +13 -0
- package/src/common/utils/convertSatoshi.ts +21 -0
- package/src/common/utils/getErrorMessage.ts +35 -0
- package/src/common/utils/isValidChain.ts +5 -0
- package/src/index.ts +5 -0
- package/src/provider/Provider.ts +174 -0
- package/src/provider/ReadProvider.ts +120 -0
- package/src/provider/index.ts +2 -0
- package/src/provider/rpcUrlConfig.ts +8 -0
- package/src/provider/types.ts +58 -0
- package/src/provider/utils/getMaxPriorityFeePerGas.ts +25 -0
- package/src/sdk/apiConfig.ts +20 -0
- package/src/sdk/generateDepositBtcAddress/generateDepositBtcAddress.stories.tsx +52 -0
- package/src/sdk/generateDepositBtcAddress/generateDepositBtcAddress.ts +64 -0
- package/src/sdk/generateDepositBtcAddress/index.ts +1 -0
- package/src/sdk/getDepositBtcAddress/getDepositBtcAddress.stories.tsx +53 -0
- package/src/sdk/getDepositBtcAddress/getDepositBtcAddress.ts +101 -0
- package/src/sdk/getDepositBtcAddress/index.ts +1 -0
- package/src/sdk/getDepositsByAddress/getDepositsByAddress.stories.tsx +49 -0
- package/src/sdk/getDepositsByAddress/getDepositsByAddress.ts +146 -0
- package/src/sdk/getDepositsByAddress/index.ts +1 -0
- package/src/sdk/index.ts +3 -0
- package/src/sdk/internalTypes.ts +5 -0
- package/src/sdk/utils/getCainIdByName.ts +21 -0
- package/src/sdk/utils/getChainNameById.ts +17 -0
- package/src/stories/components/Button/Button.tsx +43 -0
- package/src/stories/components/Button/index.ts +1 -0
- package/src/stories/components/CodeBlock/CodeBlock.tsx +24 -0
- package/src/stories/components/CodeBlock/CodeBlockStyles.css +3 -0
- package/src/stories/components/CodeBlock/index.ts +1 -0
- package/src/stories/components/Spinner/Spinner.tsx +25 -0
- package/src/stories/components/Spinner/index.ts +1 -0
- package/src/stories/const.ts +1 -0
- package/src/stories/hooks/useConnect.ts +47 -0
- package/src/stories/hooks/useQuery.ts +56 -0
- package/src/stories/utils/connectInjectedWallet.ts +12 -0
- package/src/stories/utils/fromCamelCase.ts +16 -0
- package/src/stories/utils/getMetaTitle.ts +7 -0
- package/src/stories/utils/getWalletInfo.ts +31 -0
- package/src/vite-env.d.ts +1 -0
- package/src/web3Sdk/abi/IERC20.json +222 -0
- package/src/web3Sdk/abi/LBTC.json +1400 -0
- package/src/web3Sdk/abi/index.ts +4 -0
- package/src/web3Sdk/approveLBTC/approveLBTC.stories.tsx +77 -0
- package/src/web3Sdk/approveLBTC/approveLBTC.ts +48 -0
- package/src/web3Sdk/approveLBTC/index.ts +1 -0
- package/src/web3Sdk/claimLBTC/claimLBTC.stories.tsx +77 -0
- package/src/web3Sdk/claimLBTC/claimLBTC.ts +62 -0
- package/src/web3Sdk/claimLBTC/index.ts +1 -0
- package/src/web3Sdk/index.ts +4 -0
- package/src/web3Sdk/internalTypes.ts +19 -0
- package/src/web3Sdk/lbtcAddressConfig.ts +16 -0
- package/src/web3Sdk/signLbtcDestionationAddr/index.ts +1 -0
- package/src/web3Sdk/signLbtcDestionationAddr/signLbtcDestionationAddr.stories.tsx +97 -0
- package/src/web3Sdk/signLbtcDestionationAddr/signLbtcDestionationAddr.ts +25 -0
- package/src/web3Sdk/unstakeLBTC/index.ts +1 -0
- package/src/web3Sdk/unstakeLBTC/unstakeLBTC.stories.tsx +77 -0
- package/src/web3Sdk/unstakeLBTC/unstakeLBTC.ts +51 -0
- package/src/web3Sdk/utils/getGasMultiplier.ts +19 -0
- package/src/web3Sdk/utils/getLbtcTokenContract.ts +28 -0
- package/src/web3Sdk/utils/getTokenABI.ts +12 -0
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lombard.finance/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"exports": {
|
|
5
|
+
".": {
|
|
6
|
+
"import": "./dist/index.js",
|
|
7
|
+
"types": "./src/index.ts"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"types": "./src/index.ts",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"src"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">= 18"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"types": "tsc --noEmit",
|
|
22
|
+
"build": "yarn types && vite build",
|
|
23
|
+
"storybook": "storybook dev -p 6006",
|
|
24
|
+
"build-storybook": "storybook build",
|
|
25
|
+
"build-docs": "rimraf ./docs && typedoc"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@bitcoin-js/tiny-secp256k1-asmjs": "^2.2.3",
|
|
29
|
+
"bitcoinjs-lib": "^6.1.5"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"axios": "^1",
|
|
33
|
+
"bignumber.js": "^9",
|
|
34
|
+
"web3": "^4"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@chromatic-com/storybook": "1.6.1",
|
|
38
|
+
"@storybook/addon-essentials": "^8.2.9",
|
|
39
|
+
"@storybook/addon-interactions": "^8.2.9",
|
|
40
|
+
"@storybook/addon-links": "^8.2.9",
|
|
41
|
+
"@storybook/addon-onboarding": "^8.2.9",
|
|
42
|
+
"@storybook/blocks": "^8.2.9",
|
|
43
|
+
"@storybook/react": "^8.2.9",
|
|
44
|
+
"@storybook/react-vite": "^8.2.9",
|
|
45
|
+
"@storybook/test": "^8.2.9",
|
|
46
|
+
"axios": "^1.7.4",
|
|
47
|
+
"bignumber.js": "^9.1.2",
|
|
48
|
+
"bootstrap": "^5.3.3",
|
|
49
|
+
"rimraf": "^5",
|
|
50
|
+
"storybook": "^8.2.9",
|
|
51
|
+
"typedoc": "^0.26.6",
|
|
52
|
+
"typescript": "^5.4.5",
|
|
53
|
+
"web3": "^4.11.1"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as ecc from '@bitcoin-js/tiny-secp256k1-asmjs';
|
|
2
|
+
import {
|
|
3
|
+
address as addressUtils,
|
|
4
|
+
initEccLib,
|
|
5
|
+
networks,
|
|
6
|
+
payments,
|
|
7
|
+
} from 'bitcoinjs-lib';
|
|
8
|
+
import { OEnv, TEnv } from '../../common/types/types';
|
|
9
|
+
|
|
10
|
+
initEccLib(ecc);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get output script from address.
|
|
14
|
+
*
|
|
15
|
+
* @param address - The address.
|
|
16
|
+
* @param networkMode - The network mode.
|
|
17
|
+
*
|
|
18
|
+
* @returns The output script.
|
|
19
|
+
*/
|
|
20
|
+
export function getOutputScript(
|
|
21
|
+
address: string,
|
|
22
|
+
env: TEnv = OEnv.prod,
|
|
23
|
+
): string {
|
|
24
|
+
const paymentType = getPaymentType(address);
|
|
25
|
+
|
|
26
|
+
const payment = payments[paymentType]({
|
|
27
|
+
address,
|
|
28
|
+
network: env === OEnv.prod ? networks.bitcoin : networks.testnet,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const paymentOutputScript = payment.output?.toString('hex');
|
|
32
|
+
|
|
33
|
+
if (!paymentOutputScript) {
|
|
34
|
+
throw new Error('Output script is not found.');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return `0x${paymentOutputScript}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getPaymentType(address: string): 'p2tr' | 'p2wpkh' {
|
|
41
|
+
const result = addressUtils.fromBech32(address);
|
|
42
|
+
|
|
43
|
+
const isP2TR = result.version === 1 && result.data.length === 32;
|
|
44
|
+
if (isP2TR) {
|
|
45
|
+
return 'p2tr';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const isP2WPKH = result.version === 0 && result.data.length === 20;
|
|
49
|
+
if (isP2WPKH) {
|
|
50
|
+
return 'p2wpkh';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
throw new Error('Payment type is not supported.');
|
|
54
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const OEnv = {
|
|
2
|
+
prod: 'prod',
|
|
3
|
+
stage: 'stage',
|
|
4
|
+
} as const;
|
|
5
|
+
|
|
6
|
+
export type TEnv = (typeof OEnv)[keyof typeof OEnv];
|
|
7
|
+
|
|
8
|
+
export const OChainId = {
|
|
9
|
+
ethereum: 1,
|
|
10
|
+
holesky: 17000,
|
|
11
|
+
} as const;
|
|
12
|
+
|
|
13
|
+
export type TChainId = (typeof OChainId)[keyof typeof OChainId];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const BTC_DECIMALS = 8;
|
|
2
|
+
const SATOSHI_SCALE = 10 ** BTC_DECIMALS;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Convert Satoshi to BTC
|
|
6
|
+
* @param amount - Satoshi amount
|
|
7
|
+
* @returns BTC amount
|
|
8
|
+
*/
|
|
9
|
+
export function fromSatoshi(amount: number | string) {
|
|
10
|
+
return +amount / SATOSHI_SCALE;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Convert BTC to Satoshi
|
|
15
|
+
*
|
|
16
|
+
* @param amount - BTC amount
|
|
17
|
+
* @returns Satoshi amount
|
|
18
|
+
*/
|
|
19
|
+
export function toSatoshi(amount: number | string) {
|
|
20
|
+
return Math.floor(+amount * SATOSHI_SCALE);
|
|
21
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AxiosError } from 'axios';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Retrieves the error message from the given error object.
|
|
5
|
+
*
|
|
6
|
+
* @param error - The error object.
|
|
7
|
+
* @returns The error message as a string.
|
|
8
|
+
*/
|
|
9
|
+
export function getErrorMessage(error: unknown): string {
|
|
10
|
+
if (typeof error === 'string') {
|
|
11
|
+
return error;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (error instanceof Error) {
|
|
15
|
+
return getAxiosErrorMessage(error as AxiosError);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return getErrorMessageFromObject(error);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getAxiosErrorMessage(error: AxiosError): string {
|
|
22
|
+
if (error.response) {
|
|
23
|
+
return (error.response.data as { message: string }).message;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return error.message;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getErrorMessageFromObject(error: any): string {
|
|
30
|
+
if (error?.message) {
|
|
31
|
+
return error.message;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return 'Unknown error';
|
|
35
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import Web3, { Contract, ContractAbi, Transaction, utils } from 'web3';
|
|
2
|
+
import { IReadProviderParams, ReadProvider } from './ReadProvider';
|
|
3
|
+
import {
|
|
4
|
+
TRpcUrlConfig,
|
|
5
|
+
rpcUrlConfig as defaultRpcUrlConfig,
|
|
6
|
+
} from './rpcUrlConfig';
|
|
7
|
+
import { ISendOptions, IWeb3SendResult } from './types';
|
|
8
|
+
|
|
9
|
+
export interface IProviderParams extends IReadProviderParams {
|
|
10
|
+
/**
|
|
11
|
+
* The web3 instance with write access.
|
|
12
|
+
*/
|
|
13
|
+
web3: Web3;
|
|
14
|
+
/**
|
|
15
|
+
* The сurrent account address.
|
|
16
|
+
*/
|
|
17
|
+
account: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class Provider extends ReadProvider {
|
|
21
|
+
web3: Web3;
|
|
22
|
+
account: string;
|
|
23
|
+
rpcConfig: TRpcUrlConfig;
|
|
24
|
+
|
|
25
|
+
constructor({ web3, account, chainId, rpcUrlConfig }: IProviderParams) {
|
|
26
|
+
super({ chainId, rpcUrlConfig });
|
|
27
|
+
this.web3 = web3;
|
|
28
|
+
this.account = account;
|
|
29
|
+
this.chainId = chainId;
|
|
30
|
+
this.rpcConfig = { ...defaultRpcUrlConfig, ...rpcUrlConfig };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Signs a message using the current provider and account.
|
|
35
|
+
* @public
|
|
36
|
+
* @param message - The message to be signed.
|
|
37
|
+
* @returns A promise that resolves to the signed message as a string.
|
|
38
|
+
*/
|
|
39
|
+
public async signMessage(message: string): Promise<string> {
|
|
40
|
+
const { account } = this;
|
|
41
|
+
|
|
42
|
+
const messageHex = `0x${Buffer.from(message, 'utf8').toString('hex')}`;
|
|
43
|
+
|
|
44
|
+
const ethereum = this.web3.currentProvider as any;
|
|
45
|
+
|
|
46
|
+
return ethereum.request({
|
|
47
|
+
method: 'personal_sign',
|
|
48
|
+
params: [messageHex, account],
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Custom replacement for web3js [send](https://docs.web3js.org/libdocs/Contract#send).
|
|
54
|
+
*
|
|
55
|
+
* @public
|
|
56
|
+
* @param {string} from - Address of the sender.
|
|
57
|
+
* @param {string} to - Address of the recipient.
|
|
58
|
+
* @param {ISendOptions} sendOptions - Options for sending transaction.
|
|
59
|
+
* @returns {Promise<IWeb3SendResult>} Promise with transaction hash and receipt promise.
|
|
60
|
+
*/
|
|
61
|
+
public async sendTransactionAsync(
|
|
62
|
+
from: string,
|
|
63
|
+
to: string,
|
|
64
|
+
sendOptions: ISendOptions,
|
|
65
|
+
): Promise<IWeb3SendResult> {
|
|
66
|
+
const { chainId, web3: web3Write } = this;
|
|
67
|
+
const web3Read = this.getReadWeb3();
|
|
68
|
+
|
|
69
|
+
const {
|
|
70
|
+
data,
|
|
71
|
+
estimate = false,
|
|
72
|
+
estimateFee = false,
|
|
73
|
+
extendedGasLimit,
|
|
74
|
+
gasLimit = '0',
|
|
75
|
+
value = '0',
|
|
76
|
+
gasLimitMultiplier = 1,
|
|
77
|
+
} = sendOptions;
|
|
78
|
+
let { nonce } = sendOptions;
|
|
79
|
+
|
|
80
|
+
if (!nonce) {
|
|
81
|
+
nonce = await web3Read.eth.getTransactionCount(from);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.log(`Nonce: ${nonce}`);
|
|
85
|
+
|
|
86
|
+
const tx: Transaction = {
|
|
87
|
+
from,
|
|
88
|
+
to,
|
|
89
|
+
value: utils.numberToHex(value),
|
|
90
|
+
gas: utils.numberToHex(gasLimit),
|
|
91
|
+
data,
|
|
92
|
+
nonce,
|
|
93
|
+
chainId: utils.numberToHex(chainId),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
if (estimate) {
|
|
97
|
+
try {
|
|
98
|
+
const estimatedGas = await web3Read.eth.estimateGas(tx);
|
|
99
|
+
const multipliedGasLimit = Math.round(
|
|
100
|
+
Number(estimatedGas) * gasLimitMultiplier,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
if (extendedGasLimit) {
|
|
104
|
+
tx.gas = utils.numberToHex(multipliedGasLimit + extendedGasLimit);
|
|
105
|
+
} else {
|
|
106
|
+
tx.gas = utils.numberToHex(multipliedGasLimit);
|
|
107
|
+
}
|
|
108
|
+
} catch (e) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
(e as Partial<Error>).message ??
|
|
111
|
+
'Failed to estimate gas limit for transaction.',
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const { maxFeePerGas, maxPriorityFeePerGas } = estimateFee
|
|
117
|
+
? await this.getMaxFees().catch(() => sendOptions)
|
|
118
|
+
: sendOptions;
|
|
119
|
+
|
|
120
|
+
if (maxPriorityFeePerGas !== undefined) {
|
|
121
|
+
tx.maxPriorityFeePerGas = utils.numberToHex(maxPriorityFeePerGas);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (maxFeePerGas !== undefined) {
|
|
125
|
+
tx.maxFeePerGas = utils.numberToHex(maxFeePerGas);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!tx.maxFeePerGas && !tx.maxPriorityFeePerGas) {
|
|
129
|
+
const safeGasPrice = await this.getSafeGasPriceWei();
|
|
130
|
+
tx.gasPrice = safeGasPrice.toString(10);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!tx.maxFeePerGas && !tx.maxPriorityFeePerGas) {
|
|
134
|
+
const safeGasPrice = await this.getSafeGasPriceWei();
|
|
135
|
+
tx.gasPrice = safeGasPrice.toString(10);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
console.log('Sending transaction via Web3: ', tx);
|
|
139
|
+
|
|
140
|
+
return new Promise((resolve, reject) => {
|
|
141
|
+
const promise = web3Write.eth.sendTransaction(tx);
|
|
142
|
+
|
|
143
|
+
promise
|
|
144
|
+
.once('transactionHash', async (transactionHash: string) => {
|
|
145
|
+
console.log(`Just signed transaction has is: ${transactionHash}`);
|
|
146
|
+
|
|
147
|
+
const rawTx = await web3Read.eth.getTransaction(transactionHash);
|
|
148
|
+
|
|
149
|
+
console.log(
|
|
150
|
+
'Found transaction in node: ',
|
|
151
|
+
JSON.stringify(
|
|
152
|
+
rawTx,
|
|
153
|
+
(_, value) =>
|
|
154
|
+
typeof value === 'bigint' ? value.toString() : value,
|
|
155
|
+
2,
|
|
156
|
+
),
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
resolve({
|
|
160
|
+
receiptPromise: promise,
|
|
161
|
+
transactionHash,
|
|
162
|
+
});
|
|
163
|
+
})
|
|
164
|
+
.catch(reject);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
public createContract<AbiType extends ContractAbi>(
|
|
169
|
+
abi: any,
|
|
170
|
+
address: string,
|
|
171
|
+
): Contract<AbiType> {
|
|
172
|
+
return new this.web3.eth.Contract<AbiType>(abi, address);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js';
|
|
2
|
+
import Web3, { Contract, ContractAbi } from 'web3';
|
|
3
|
+
import {
|
|
4
|
+
TRpcUrlConfig,
|
|
5
|
+
rpcUrlConfig as defaultRpcUrlConfig,
|
|
6
|
+
} from './rpcUrlConfig';
|
|
7
|
+
import { IGetMaxFeesResult } from './types';
|
|
8
|
+
import { getMaxPriorityFeePerGas } from './utils/getMaxPriorityFeePerGas';
|
|
9
|
+
|
|
10
|
+
const FEE_MULTIPLIER = 2;
|
|
11
|
+
const ADDITIONAL_SAFE_GAS_PRICE_WEI = 25_000;
|
|
12
|
+
|
|
13
|
+
export interface IReadProviderParams {
|
|
14
|
+
chainId: number;
|
|
15
|
+
/**
|
|
16
|
+
* The RPC URL configuration. If not provided, the default configuration will be used.
|
|
17
|
+
*/
|
|
18
|
+
rpcUrlConfig?: TRpcUrlConfig;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class ReadProvider {
|
|
22
|
+
chainId: number;
|
|
23
|
+
rpcConfig: TRpcUrlConfig;
|
|
24
|
+
|
|
25
|
+
constructor({ chainId, rpcUrlConfig }: IReadProviderParams) {
|
|
26
|
+
this.chainId = chainId;
|
|
27
|
+
this.rpcConfig = { ...defaultRpcUrlConfig, ...rpcUrlConfig };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Returns web3 instance for read operations.
|
|
32
|
+
*
|
|
33
|
+
* @public
|
|
34
|
+
* @returns {Web3} Web3 instance.
|
|
35
|
+
*/
|
|
36
|
+
public getReadWeb3(): Web3 {
|
|
37
|
+
const rpcUrl = this.getRpcUrl();
|
|
38
|
+
const readWeb3 = new Web3();
|
|
39
|
+
const provider = new Web3.providers.HttpProvider(rpcUrl);
|
|
40
|
+
readWeb3.setProvider(provider);
|
|
41
|
+
return readWeb3;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Retrieves the RPC URL based on the current chain ID.
|
|
46
|
+
* @returns The RPC URL for the current chain ID.
|
|
47
|
+
* @throws Error if the RPC URL for the current chain ID is not found.
|
|
48
|
+
*/
|
|
49
|
+
getRpcUrl(): string {
|
|
50
|
+
const { chainId } = this;
|
|
51
|
+
const rpcUrl = this.rpcConfig?.[chainId];
|
|
52
|
+
|
|
53
|
+
if (!rpcUrl) {
|
|
54
|
+
throw new Error(`RPC URL for chainId ${chainId} not found`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return rpcUrl;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Calculates max fees for transaction. Thess values are available for networks
|
|
62
|
+
* with EIP-1559 support.
|
|
63
|
+
*
|
|
64
|
+
* @public
|
|
65
|
+
* @note If current network is Binance Smart Chain, will return default values.
|
|
66
|
+
* @returns {Promise<IGetMaxFeesResult>} Max fees for transaction.
|
|
67
|
+
*/
|
|
68
|
+
public async getMaxFees(): Promise<IGetMaxFeesResult> {
|
|
69
|
+
const web3 = this.getReadWeb3();
|
|
70
|
+
const rpcUrl = this.getRpcUrl();
|
|
71
|
+
|
|
72
|
+
const [block, maxPriorityFeePerGas] = await Promise.all([
|
|
73
|
+
web3.eth.getBlock('latest'),
|
|
74
|
+
getMaxPriorityFeePerGas(rpcUrl),
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
if (!block?.baseFeePerGas && typeof block?.baseFeePerGas !== 'bigint') {
|
|
78
|
+
return {};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const maxFeePerGas = new BigNumber(block.baseFeePerGas.toString(10))
|
|
82
|
+
.multipliedBy(FEE_MULTIPLIER)
|
|
83
|
+
.plus(maxPriorityFeePerGas);
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
maxFeePerGas: +maxFeePerGas,
|
|
87
|
+
maxPriorityFeePerGas: +maxPriorityFeePerGas,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Returns safe gas price for transaction.
|
|
93
|
+
*
|
|
94
|
+
* @public
|
|
95
|
+
* @returns {Promise<BigNumber>} Safe gas price.
|
|
96
|
+
*/
|
|
97
|
+
public async getSafeGasPriceWei(): Promise<BigNumber> {
|
|
98
|
+
const pureGasPriceWei = await this.getReadWeb3().eth.getGasPrice();
|
|
99
|
+
|
|
100
|
+
return new BigNumber(pureGasPriceWei.toString(10)).plus(
|
|
101
|
+
ADDITIONAL_SAFE_GAS_PRICE_WEI,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Creates a contract instance with the given ABI and address.
|
|
107
|
+
*
|
|
108
|
+
* @template AbiType - The type of the contract ABI.
|
|
109
|
+
* @param {any} abi - The ABI of the contract.
|
|
110
|
+
* @param {string} address - The address of the contract.
|
|
111
|
+
* @returns {Contract<AbiType>} The contract instance.
|
|
112
|
+
*/
|
|
113
|
+
public createContract<AbiType extends ContractAbi>(
|
|
114
|
+
abi: any,
|
|
115
|
+
address: string,
|
|
116
|
+
): Contract<AbiType> {
|
|
117
|
+
const web3 = this.getReadWeb3();
|
|
118
|
+
return new web3.eth.Contract<AbiType>(abi, address);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { OChainId } from '../common/types/types';
|
|
2
|
+
|
|
3
|
+
export type TRpcUrlConfig = Record<number, string>;
|
|
4
|
+
|
|
5
|
+
export const rpcUrlConfig: TRpcUrlConfig = {
|
|
6
|
+
[OChainId.ethereum]: 'https://eth.llamarpc.com',
|
|
7
|
+
[OChainId.holesky]: 'https://ethereum-holesky-rpc.publicnode.com',
|
|
8
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { core, eth, FMT_BYTES, FMT_NUMBER, TransactionReceipt } from 'web3';
|
|
2
|
+
|
|
3
|
+
export interface IGetMaxFeesResult {
|
|
4
|
+
maxPriorityFeePerGas?: number;
|
|
5
|
+
maxFeePerGas?: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type PromiEvent<T> = core.Web3PromiEvent<
|
|
9
|
+
T,
|
|
10
|
+
eth.SendTransactionEvents<{
|
|
11
|
+
readonly number: FMT_NUMBER.BIGINT;
|
|
12
|
+
readonly bytes: FMT_BYTES.HEX;
|
|
13
|
+
}>
|
|
14
|
+
>;
|
|
15
|
+
|
|
16
|
+
export interface IWeb3SendResult {
|
|
17
|
+
receiptPromise: PromiEvent<TransactionReceipt>;
|
|
18
|
+
transactionHash: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ISendOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Txn data.
|
|
24
|
+
*/
|
|
25
|
+
data?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Gas limit for transaction.
|
|
28
|
+
* @note When `estimate` is `true`, this value will redefined by estimated gas limit.
|
|
29
|
+
*/
|
|
30
|
+
gasLimit?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Multiplier for gas limit when estimating gas.
|
|
33
|
+
* When specified, gas limit will be calculated as `gasLimit * gasLimitMultiplier`.
|
|
34
|
+
* @note works only when `estimate` is `true`.
|
|
35
|
+
* @default 1
|
|
36
|
+
*/
|
|
37
|
+
gasLimitMultiplier?: number;
|
|
38
|
+
value?: string;
|
|
39
|
+
/**
|
|
40
|
+
* When `true`, will estimate gas limit for transaction.
|
|
41
|
+
* @note `gasLimit` option will be redefined by estimated gas limit.
|
|
42
|
+
*/
|
|
43
|
+
estimate?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* When `true`, will estimate max fees for transaction.
|
|
46
|
+
* @note `maxPriorityFeePerGas` and `maxFeePerGas` options will be redefined by estimated values.
|
|
47
|
+
* @note Please use only for networks with EIP-1559 support.
|
|
48
|
+
*/
|
|
49
|
+
estimateFee?: boolean;
|
|
50
|
+
nonce?: bigint;
|
|
51
|
+
/**
|
|
52
|
+
* Additional gas limit for transaction.
|
|
53
|
+
* @note When `estimate` is `true`, this value will be added to estimated gas limit.
|
|
54
|
+
*/
|
|
55
|
+
extendedGasLimit?: number;
|
|
56
|
+
maxPriorityFeePerGas?: number;
|
|
57
|
+
maxFeePerGas?: number;
|
|
58
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js';
|
|
2
|
+
import Web3 from 'web3';
|
|
3
|
+
|
|
4
|
+
export async function getMaxPriorityFeePerGas(
|
|
5
|
+
rpcUrl: string,
|
|
6
|
+
): Promise<BigNumber> {
|
|
7
|
+
const response = await fetch(rpcUrl, {
|
|
8
|
+
method: 'POST',
|
|
9
|
+
headers: {
|
|
10
|
+
'Content-Type': 'application/json',
|
|
11
|
+
},
|
|
12
|
+
body: JSON.stringify({
|
|
13
|
+
jsonrpc: '2.0',
|
|
14
|
+
id: 1,
|
|
15
|
+
method: 'eth_maxPriorityFeePerGas',
|
|
16
|
+
params: [],
|
|
17
|
+
}),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const data = await response.json();
|
|
21
|
+
|
|
22
|
+
const convertedHexValue = Web3.utils.hexToNumber(data?.result);
|
|
23
|
+
|
|
24
|
+
return new BigNumber(Number(convertedHexValue));
|
|
25
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defaultEnv } from '../common/const';
|
|
2
|
+
import { TEnv } from '../common/types/types';
|
|
3
|
+
|
|
4
|
+
interface IApiConfig {
|
|
5
|
+
depositAddrApiUrl: string;
|
|
6
|
+
baseApiUrl: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const stageConfig: IApiConfig = {
|
|
10
|
+
depositAddrApiUrl: 'https://staging.prod.lombard.finance',
|
|
11
|
+
baseApiUrl: 'https://staging.prod.lombard.finance',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const prodConfig: IApiConfig = {
|
|
15
|
+
depositAddrApiUrl: 'https://consortium.lombard.finance',
|
|
16
|
+
baseApiUrl: 'https://mainnet.prod.lombard.finance',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const getApiConfig = (env: TEnv = defaultEnv): IApiConfig =>
|
|
20
|
+
env === 'prod' ? prodConfig : stageConfig;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { defaultEnv } from '../../common/const';
|
|
3
|
+
import { OChainId } from '../../common/types/types';
|
|
4
|
+
import { Button } from '../../stories/components/Button';
|
|
5
|
+
import { CodeBlock } from '../../stories/components/CodeBlock';
|
|
6
|
+
import { exampleEvmAddress } from '../../stories/const';
|
|
7
|
+
import useQuery from '../../stories/hooks/useQuery';
|
|
8
|
+
import { fromCamelCase } from '../../stories/utils/fromCamelCase';
|
|
9
|
+
import {
|
|
10
|
+
generateDepositBtcAddress,
|
|
11
|
+
IGenerateDepositBtcAddressParams,
|
|
12
|
+
} from './generateDepositBtcAddress';
|
|
13
|
+
|
|
14
|
+
const { name } = generateDepositBtcAddress;
|
|
15
|
+
const nameWithWhitespaces = fromCamelCase(name);
|
|
16
|
+
|
|
17
|
+
const meta = {
|
|
18
|
+
title: 'SDK/generateDepositBtcAddress',
|
|
19
|
+
component: StoryView,
|
|
20
|
+
tags: ['autodocs'],
|
|
21
|
+
} satisfies Meta<typeof StoryView>;
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
|
|
25
|
+
type Story = StoryObj<typeof meta>;
|
|
26
|
+
|
|
27
|
+
export const WithParams: Story = {
|
|
28
|
+
args: {
|
|
29
|
+
address: exampleEvmAddress,
|
|
30
|
+
chainId: OChainId.ethereum,
|
|
31
|
+
signature: '',
|
|
32
|
+
env: defaultEnv,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function StoryView(props: IGenerateDepositBtcAddressParams) {
|
|
37
|
+
const { data, error, isLoading, refetch } = useQuery(
|
|
38
|
+
() => generateDepositBtcAddress(props),
|
|
39
|
+
[props],
|
|
40
|
+
false,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<>
|
|
45
|
+
<Button onClick={refetch} disabled={isLoading} isLoading={isLoading}>
|
|
46
|
+
{nameWithWhitespaces}
|
|
47
|
+
</Button>
|
|
48
|
+
|
|
49
|
+
<CodeBlock text={error || data} />
|
|
50
|
+
</>
|
|
51
|
+
);
|
|
52
|
+
}
|