@octaflowlabs/onchain-sdk 1.0.0-test7 → 1.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.
Files changed (39) hide show
  1. package/dist/blockchain/broadcastTransaction.d.ts +1 -1
  2. package/dist/blockchain/broadcastTransaction.js +4 -2
  3. package/dist/blockchain/buildUnsignedTransferTx.d.ts +2 -1
  4. package/dist/blockchain/buildUnsignedTransferTx.js +116 -72
  5. package/dist/blockchain/estimateGasLimitFromProvider.js +15 -2
  6. package/dist/blockchain/getProvider.d.ts +1 -1
  7. package/dist/blockchain/getProvider.js +13 -4
  8. package/dist/blockchain/txStatus.js +2 -0
  9. package/dist/cjs/blockchain/broadcastTransaction.d.ts +1 -1
  10. package/dist/cjs/blockchain/broadcastTransaction.js +4 -2
  11. package/dist/cjs/blockchain/buildUnsignedTransferTx.d.ts +2 -1
  12. package/dist/cjs/blockchain/buildUnsignedTransferTx.js +118 -73
  13. package/dist/cjs/blockchain/estimateGasLimitFromProvider.js +15 -2
  14. package/dist/cjs/blockchain/getProvider.d.ts +1 -1
  15. package/dist/cjs/blockchain/getProvider.js +12 -3
  16. package/dist/cjs/blockchain/txStatus.js +2 -0
  17. package/dist/cjs/index.d.ts +7 -2
  18. package/dist/cjs/index.js +12 -1
  19. package/dist/cjs/services/evm-wallet-core/entropy.d.ts +3 -0
  20. package/dist/cjs/services/evm-wallet-core/entropy.js +2 -0
  21. package/dist/cjs/services/evm-wallet-core/evmWalletService.d.ts +27 -0
  22. package/dist/cjs/services/evm-wallet-core/evmWalletService.js +90 -0
  23. package/dist/cjs/services/evm-wallet-core/signer.d.ts +7 -0
  24. package/dist/cjs/services/evm-wallet-core/signer.js +17 -0
  25. package/dist/cjs/types/common.d.ts +18 -0
  26. package/dist/cjs/utils/formatAmount.d.ts +6 -0
  27. package/dist/cjs/utils/formatAmount.js +55 -0
  28. package/dist/index.d.ts +7 -2
  29. package/dist/index.js +5 -1
  30. package/dist/services/evm-wallet-core/entropy.d.ts +3 -0
  31. package/dist/services/evm-wallet-core/entropy.js +1 -0
  32. package/dist/services/evm-wallet-core/evmWalletService.d.ts +27 -0
  33. package/dist/services/evm-wallet-core/evmWalletService.js +86 -0
  34. package/dist/services/evm-wallet-core/signer.d.ts +7 -0
  35. package/dist/services/evm-wallet-core/signer.js +11 -0
  36. package/dist/types/common.d.ts +18 -0
  37. package/dist/utils/formatAmount.d.ts +6 -0
  38. package/dist/utils/formatAmount.js +50 -0
  39. package/package.json +1 -1
@@ -1,2 +1,2 @@
1
1
  import { BroadcastTransactionOptions } from '../types/common';
2
- export declare const broadcastTransaction: ({ signedTx, rpcUrl, waitConfirmations, }: BroadcastTransactionOptions) => Promise<string>;
2
+ export declare const broadcastTransaction: ({ signedTx, rpcUrl, chainId, waitConfirmations, }: BroadcastTransactionOptions) => Promise<string>;
@@ -2,8 +2,10 @@
2
2
  import { Transaction } from 'ethers';
3
3
  /** local imports */
4
4
  import { getProvider } from './getProvider';
5
- export const broadcastTransaction = async ({ signedTx, rpcUrl, waitConfirmations = 0, }) => {
6
- const provider = getProvider(rpcUrl);
5
+ export const broadcastTransaction = async ({ signedTx, rpcUrl, chainId, waitConfirmations = 0, }) => {
6
+ const provider = getProvider(rpcUrl, chainId);
7
+ if (!provider)
8
+ throw new Error('Could not create provider with given rpcUrl');
7
9
  try {
8
10
  Transaction.from(signedTx);
9
11
  }
@@ -1,2 +1,3 @@
1
- import { BuildUnsignedTransferTxOptions, UnsignedTransferTxResponse } from '../types/common';
1
+ import { BuildMaxNativeTransferTxOptions, BuildMaxNativeTransferTxResponse, BuildUnsignedTransferTxOptions, UnsignedTransferTxResponse } from '../types/common';
2
2
  export declare const buildUnsignedTransferTx: (options: BuildUnsignedTransferTxOptions) => Promise<UnsignedTransferTxResponse>;
3
+ export declare const buildMaxNativeTransferTx: (options: BuildMaxNativeTransferTxOptions) => Promise<BuildMaxNativeTransferTxResponse>;
@@ -1,88 +1,132 @@
1
1
  /** npm imports */
2
- import { Interface, parseUnits } from 'ethers';
2
+ import { Interface, formatUnits, parseUnits } from 'ethers';
3
3
  /** local imports */
4
4
  import { getProvider } from './getProvider';
5
5
  import { estimateGasLimitFromProvider } from './estimateGasLimitFromProvider';
6
6
  export const buildUnsignedTransferTx = async (options) => {
7
+ console.log({ options });
7
8
  const provider = getProvider(options.rpcUrl, options.chainId);
8
- let unsignedTx = { to: options.tokenAddress };
9
- const isNativeToken = !options.tokenAddress ||
10
- options.tokenAddress?.toLowerCase() === '0x0000000000000000000000000000000000000000' ||
11
- options.tokenAddress?.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
12
- if (!isNativeToken) {
13
- if (!options.tokenAddress)
14
- throw new Error('Token address is required for token transfer');
15
- const code = await provider.getCode(options.tokenAddress);
16
- if (code === '0x' || code === '0x0')
17
- throw new Error('Invalid token address');
18
- const erc20Interface = new Interface(['function transfer(address to, uint256 amount)']);
19
- const decimals = options.tokenDecimals ?? 18;
20
- if (!options.value)
21
- throw new Error('Value is required for token transfer');
22
- const amountParsed = parseUnits(options.value, decimals);
23
- unsignedTx = {
24
- ...unsignedTx,
25
- data: erc20Interface.encodeFunctionData('transfer', [options.toAddress, amountParsed]),
26
- value: 0n,
27
- };
28
- }
29
- else {
30
- if (!options.value)
31
- throw new Error('Native transfer requires value');
32
- unsignedTx = {
33
- to: options.toAddress,
34
- data: '0x',
35
- value: parseUnits(options.value, 18),
36
- };
37
- }
38
- if (options.chainId)
39
- unsignedTx.chainId = options.chainId;
40
- const nonce = await provider.getTransactionCount(options.fromAddress, 'pending');
41
- const estimateGas = await estimateGasLimitFromProvider({
42
- provider: provider,
43
- unsignedTx,
44
- walletAddress: options.fromAddress,
45
- defaultGasLimit: options.defaultGasLimit,
46
- });
47
- const unsignedTxToReturn = {
48
- from: options.fromAddress,
49
- to: unsignedTx.to,
50
- data: unsignedTx.data,
51
- value: unsignedTx.value?.toString(),
52
- gasLimit: estimateGas.gasLimit.toString(),
53
- chainId: unsignedTx.chainId,
54
- nonce,
55
- maxFeePerGas: estimateGas.feeData.maxFeePerGas?.toString(),
56
- maxPriorityFeePerGas: estimateGas.feeData.maxPriorityFeePerGas?.toString(),
57
- };
58
- if (estimateGas.feeData.gasPrice)
59
- unsignedTxToReturn.gasPrice = estimateGas.feeData.gasPrice.toString();
60
- console.log('Built unsigned transfer transaction:', unsignedTxToReturn);
9
+ if (!provider)
10
+ throw new Error('Could not create provider with given rpcUrl and chainId');
61
11
  try {
62
- await provider.call({
12
+ let unsignedTx = { to: options.tokenAddress };
13
+ const isNativeToken = !options.tokenAddress ||
14
+ options.tokenAddress?.toLowerCase() === '0x0000000000000000000000000000000000000000' ||
15
+ options.tokenAddress?.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
16
+ if (!isNativeToken) {
17
+ if (!options.tokenAddress)
18
+ throw new Error('Token address is required for token transfer');
19
+ const code = await provider.getCode(options.tokenAddress);
20
+ if (code === '0x' || code === '0x0')
21
+ throw new Error('Invalid token address');
22
+ const erc20Interface = new Interface(['function transfer(address to, uint256 amount)']);
23
+ const decimals = options.tokenDecimals ?? 18;
24
+ if (!options.value)
25
+ throw new Error('Value is required for token transfer');
26
+ const amountParsed = parseUnits(options.value, decimals);
27
+ unsignedTx = {
28
+ ...unsignedTx,
29
+ data: erc20Interface.encodeFunctionData('transfer', [options.toAddress, amountParsed]),
30
+ value: 0n,
31
+ };
32
+ }
33
+ else {
34
+ if (!options.value)
35
+ throw new Error('Native transfer requires value');
36
+ unsignedTx = {
37
+ to: options.toAddress,
38
+ data: '0x',
39
+ value: parseUnits(options.value, 18),
40
+ };
41
+ }
42
+ if (options.chainId)
43
+ unsignedTx.chainId = options.chainId;
44
+ const nonce = await provider.getTransactionCount(options.fromAddress, 'pending');
45
+ const estimateGas = await estimateGasLimitFromProvider({
46
+ provider: provider,
47
+ unsignedTx,
48
+ walletAddress: options.fromAddress,
49
+ defaultGasLimit: options.defaultGasLimit,
50
+ });
51
+ const unsignedTxToReturn = {
63
52
  from: options.fromAddress,
64
53
  to: unsignedTx.to,
65
54
  data: unsignedTx.data,
66
- value: unsignedTx.value,
67
- gasLimit: estimateGas.gasLimit,
68
- });
55
+ value: unsignedTx.value?.toString(),
56
+ gasLimit: estimateGas.gasLimit.toString(),
57
+ chainId: unsignedTx.chainId,
58
+ nonce,
59
+ maxFeePerGas: estimateGas.feeData.maxFeePerGas?.toString(),
60
+ maxPriorityFeePerGas: estimateGas.feeData.maxPriorityFeePerGas?.toString(),
61
+ };
62
+ if (estimateGas.feeData.gasPrice)
63
+ unsignedTxToReturn.gasPrice = estimateGas.feeData.gasPrice.toString();
64
+ const gasReserve = estimateGas.feeData.maxFeePerGas
65
+ ? estimateGas.gasLimit * estimateGas.feeData.maxFeePerGas
66
+ : estimateGas.feeData.gasPrice
67
+ ? estimateGas.gasLimit * estimateGas.feeData.gasPrice
68
+ : undefined;
69
+ console.log({ gasReserve: gasReserve?.toString() });
70
+ try {
71
+ await provider.call({
72
+ from: options.fromAddress,
73
+ to: unsignedTx.to,
74
+ data: unsignedTx.data,
75
+ value: unsignedTx.value,
76
+ gasLimit: estimateGas.gasLimit,
77
+ });
78
+ }
79
+ catch (error) {
80
+ throw new Error('Transaction would revert, provider call unsuccessful: ' + error.message || error);
81
+ }
82
+ console.log('Built unsigned transfer transaction:', unsignedTxToReturn);
83
+ return {
84
+ unsignedTx: unsignedTxToReturn,
85
+ nonce,
86
+ gasEstimated: estimateGas.gasEstimated.toString(),
87
+ gasLimit: estimateGas.gasLimit.toString(),
88
+ gasReserve: gasReserve?.toString(),
89
+ bufferPercentage: estimateGas.bufferPercentage,
90
+ feeData: {
91
+ maxFeePerGas: estimateGas.feeData.maxFeePerGas?.toString(),
92
+ maxPriorityFeePerGas: estimateGas.feeData.maxPriorityFeePerGas?.toString(),
93
+ gasPrice: estimateGas.feeData.gasPrice?.toString(),
94
+ },
95
+ // suggestedGasFees: estimateGas.suggestedGasFees,
96
+ // humanReadableFees: humanReadableGasEstimation,
97
+ };
69
98
  }
70
99
  catch (error) {
71
- throw new Error('Transaction would revert, provider call unsuccessful: ' + error.message || error);
100
+ console.error('Error building unsigned transfer transaction:', error);
101
+ throw error;
72
102
  }
73
- console.log('Built unsigned transfer transaction:', unsignedTxToReturn);
103
+ };
104
+ export const buildMaxNativeTransferTx = async (options) => {
105
+ const isNativeToken = !options.tokenAddress ||
106
+ options.tokenAddress?.toLowerCase() === '0x0000000000000000000000000000000000000000' ||
107
+ options.tokenAddress?.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
108
+ if (!isNativeToken)
109
+ throw new Error('Max native transfer requires a native token address');
110
+ const provisionalTx = await buildUnsignedTransferTx({
111
+ ...options,
112
+ value: '0',
113
+ tokenAddress: options.tokenAddress,
114
+ });
115
+ if (!provisionalTx.gasReserve)
116
+ throw new Error('Could not determine gas reserve for max send');
117
+ const balanceWei = parseUnits(options.balance, 18);
118
+ const gasReserveWei = BigInt(provisionalTx.gasReserve);
119
+ const sendableWei = balanceWei - gasReserveWei;
120
+ if (sendableWei <= 0n)
121
+ throw new Error('Insufficient balance to cover gas reserve');
122
+ const sendableValue = formatUnits(sendableWei, 18);
123
+ const finalTx = await buildUnsignedTransferTx({
124
+ ...options,
125
+ value: sendableValue,
126
+ tokenAddress: options.tokenAddress,
127
+ });
74
128
  return {
75
- unsignedTx: unsignedTxToReturn,
76
- nonce,
77
- gasEstimated: estimateGas.gasEstimated.toString(),
78
- gasLimit: estimateGas.gasLimit.toString(),
79
- bufferPercentage: estimateGas.bufferPercentage,
80
- feeData: {
81
- maxFeePerGas: estimateGas.feeData.maxFeePerGas?.toString(),
82
- maxPriorityFeePerGas: estimateGas.feeData.maxPriorityFeePerGas?.toString(),
83
- gasPrice: estimateGas.feeData.gasPrice?.toString(),
84
- },
85
- // suggestedGasFees: estimateGas.suggestedGasFees,
86
- // humanReadableFees: humanReadableGasEstimation,
129
+ ...finalTx,
130
+ sendableValue,
87
131
  };
88
132
  };
@@ -1,6 +1,9 @@
1
1
  export const estimateGasLimitFromProvider = async ({ provider, unsignedTx, walletAddress, defaultGasLimit, }) => {
2
+ let lastFeeData = null;
3
+ let lastGasEstimated = null;
2
4
  try {
3
5
  const feeData = await provider.getFeeData();
6
+ lastFeeData = feeData;
4
7
  const txForEstimation = { ...unsignedTx, from: walletAddress };
5
8
  if (feeData.maxFeePerGas !== undefined)
6
9
  txForEstimation.maxFeePerGas = feeData.maxFeePerGas;
@@ -8,7 +11,9 @@ export const estimateGasLimitFromProvider = async ({ provider, unsignedTx, walle
8
11
  txForEstimation.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;
9
12
  if (feeData.gasPrice !== undefined && txForEstimation.maxFeePerGas === undefined)
10
13
  txForEstimation.gasPrice = feeData.gasPrice;
14
+ console.log('Estimating gas with transaction:!!!!!! ', txForEstimation);
11
15
  const gasEstimated = await provider.estimateGas(txForEstimation);
16
+ lastGasEstimated = gasEstimated;
12
17
  let congestionFactor = 1.5;
13
18
  if (feeData.maxFeePerGas && feeData.maxPriorityFeePerGas) {
14
19
  try {
@@ -40,12 +45,20 @@ export const estimateGasLimitFromProvider = async ({ provider, unsignedTx, walle
40
45
  catch (error) {
41
46
  console.error('Unable to estimate gas limit: ', error);
42
47
  console.log(`Setting default gas limit to: ${defaultGasLimit}`);
48
+ const feeData = lastFeeData;
49
+ const gasEstimated = lastGasEstimated ?? defaultGasLimit;
43
50
  return {
44
- gasEstimated: defaultGasLimit,
51
+ gasEstimated,
45
52
  gasLimit: defaultGasLimit,
46
53
  bufferPercentage: 0,
47
54
  fallbackUsed: true,
48
- feeData: {},
55
+ feeData: feeData
56
+ ? {
57
+ maxFeePerGas: feeData.maxFeePerGas ?? undefined,
58
+ maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined,
59
+ gasPrice: feeData.gasPrice ?? undefined,
60
+ }
61
+ : {},
49
62
  };
50
63
  }
51
64
  };
@@ -1,3 +1,3 @@
1
1
  /** npm imports */
2
2
  import { JsonRpcProvider } from 'ethers';
3
- export declare const getProvider: (rpcUrl: string, chainId?: number) => JsonRpcProvider;
3
+ export declare const getProvider: (rpcUrl: string, chainId?: number) => JsonRpcProvider | undefined;
@@ -1,7 +1,16 @@
1
1
  /** npm imports */
2
- import { JsonRpcProvider } from 'ethers';
2
+ import { JsonRpcProvider, Network } from 'ethers';
3
3
  export const getProvider = (rpcUrl, chainId) => {
4
- const provider = new JsonRpcProvider(rpcUrl, { chainId });
5
- console.log({ provider });
6
- return provider;
4
+ try {
5
+ if (chainId) {
6
+ const network = Network.from(chainId);
7
+ return new JsonRpcProvider(rpcUrl, network, { staticNetwork: network });
8
+ }
9
+ else {
10
+ return new JsonRpcProvider(rpcUrl);
11
+ }
12
+ }
13
+ catch (error) {
14
+ console.warn('Could not create provider with chainId, falling back to rpcUrl only:', error);
15
+ }
7
16
  };
@@ -2,6 +2,8 @@
2
2
  import { getProvider } from './getProvider';
3
3
  export const txStatus = async ({ rpcUrl, txHash, chainId, }) => {
4
4
  const provider = getProvider(rpcUrl, chainId);
5
+ if (!provider)
6
+ throw new Error('Could not create provider with given rpcUrl and chainId');
5
7
  try {
6
8
  const receipt = await provider.getTransactionReceipt(txHash);
7
9
  if (!receipt)
@@ -1,2 +1,2 @@
1
1
  import { BroadcastTransactionOptions } from '../types/common';
2
- export declare const broadcastTransaction: ({ signedTx, rpcUrl, waitConfirmations, }: BroadcastTransactionOptions) => Promise<string>;
2
+ export declare const broadcastTransaction: ({ signedTx, rpcUrl, chainId, waitConfirmations, }: BroadcastTransactionOptions) => Promise<string>;
@@ -5,8 +5,10 @@ exports.broadcastTransaction = void 0;
5
5
  const ethers_1 = require("ethers");
6
6
  /** local imports */
7
7
  const getProvider_1 = require("./getProvider");
8
- const broadcastTransaction = async ({ signedTx, rpcUrl, waitConfirmations = 0, }) => {
9
- const provider = (0, getProvider_1.getProvider)(rpcUrl);
8
+ const broadcastTransaction = async ({ signedTx, rpcUrl, chainId, waitConfirmations = 0, }) => {
9
+ const provider = (0, getProvider_1.getProvider)(rpcUrl, chainId);
10
+ if (!provider)
11
+ throw new Error('Could not create provider with given rpcUrl');
10
12
  try {
11
13
  ethers_1.Transaction.from(signedTx);
12
14
  }
@@ -1,2 +1,3 @@
1
- import { BuildUnsignedTransferTxOptions, UnsignedTransferTxResponse } from '../types/common';
1
+ import { BuildMaxNativeTransferTxOptions, BuildMaxNativeTransferTxResponse, BuildUnsignedTransferTxOptions, UnsignedTransferTxResponse } from '../types/common';
2
2
  export declare const buildUnsignedTransferTx: (options: BuildUnsignedTransferTxOptions) => Promise<UnsignedTransferTxResponse>;
3
+ export declare const buildMaxNativeTransferTx: (options: BuildMaxNativeTransferTxOptions) => Promise<BuildMaxNativeTransferTxResponse>;
@@ -1,92 +1,137 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildUnsignedTransferTx = void 0;
3
+ exports.buildMaxNativeTransferTx = exports.buildUnsignedTransferTx = void 0;
4
4
  /** npm imports */
5
5
  const ethers_1 = require("ethers");
6
6
  /** local imports */
7
7
  const getProvider_1 = require("./getProvider");
8
8
  const estimateGasLimitFromProvider_1 = require("./estimateGasLimitFromProvider");
9
9
  const buildUnsignedTransferTx = async (options) => {
10
+ console.log({ options });
10
11
  const provider = (0, getProvider_1.getProvider)(options.rpcUrl, options.chainId);
11
- let unsignedTx = { to: options.tokenAddress };
12
- const isNativeToken = !options.tokenAddress ||
13
- options.tokenAddress?.toLowerCase() === '0x0000000000000000000000000000000000000000' ||
14
- options.tokenAddress?.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
15
- if (!isNativeToken) {
16
- if (!options.tokenAddress)
17
- throw new Error('Token address is required for token transfer');
18
- const code = await provider.getCode(options.tokenAddress);
19
- if (code === '0x' || code === '0x0')
20
- throw new Error('Invalid token address');
21
- const erc20Interface = new ethers_1.Interface(['function transfer(address to, uint256 amount)']);
22
- const decimals = options.tokenDecimals ?? 18;
23
- if (!options.value)
24
- throw new Error('Value is required for token transfer');
25
- const amountParsed = (0, ethers_1.parseUnits)(options.value, decimals);
26
- unsignedTx = {
27
- ...unsignedTx,
28
- data: erc20Interface.encodeFunctionData('transfer', [options.toAddress, amountParsed]),
29
- value: 0n,
30
- };
31
- }
32
- else {
33
- if (!options.value)
34
- throw new Error('Native transfer requires value');
35
- unsignedTx = {
36
- to: options.toAddress,
37
- data: '0x',
38
- value: (0, ethers_1.parseUnits)(options.value, 18),
39
- };
40
- }
41
- if (options.chainId)
42
- unsignedTx.chainId = options.chainId;
43
- const nonce = await provider.getTransactionCount(options.fromAddress, 'pending');
44
- const estimateGas = await (0, estimateGasLimitFromProvider_1.estimateGasLimitFromProvider)({
45
- provider: provider,
46
- unsignedTx,
47
- walletAddress: options.fromAddress,
48
- defaultGasLimit: options.defaultGasLimit,
49
- });
50
- const unsignedTxToReturn = {
51
- from: options.fromAddress,
52
- to: unsignedTx.to,
53
- data: unsignedTx.data,
54
- value: unsignedTx.value?.toString(),
55
- gasLimit: estimateGas.gasLimit.toString(),
56
- chainId: unsignedTx.chainId,
57
- nonce,
58
- maxFeePerGas: estimateGas.feeData.maxFeePerGas?.toString(),
59
- maxPriorityFeePerGas: estimateGas.feeData.maxPriorityFeePerGas?.toString(),
60
- };
61
- if (estimateGas.feeData.gasPrice)
62
- unsignedTxToReturn.gasPrice = estimateGas.feeData.gasPrice.toString();
63
- console.log('Built unsigned transfer transaction:', unsignedTxToReturn);
12
+ if (!provider)
13
+ throw new Error('Could not create provider with given rpcUrl and chainId');
64
14
  try {
65
- await provider.call({
15
+ let unsignedTx = { to: options.tokenAddress };
16
+ const isNativeToken = !options.tokenAddress ||
17
+ options.tokenAddress?.toLowerCase() === '0x0000000000000000000000000000000000000000' ||
18
+ options.tokenAddress?.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
19
+ if (!isNativeToken) {
20
+ if (!options.tokenAddress)
21
+ throw new Error('Token address is required for token transfer');
22
+ const code = await provider.getCode(options.tokenAddress);
23
+ if (code === '0x' || code === '0x0')
24
+ throw new Error('Invalid token address');
25
+ const erc20Interface = new ethers_1.Interface(['function transfer(address to, uint256 amount)']);
26
+ const decimals = options.tokenDecimals ?? 18;
27
+ if (!options.value)
28
+ throw new Error('Value is required for token transfer');
29
+ const amountParsed = (0, ethers_1.parseUnits)(options.value, decimals);
30
+ unsignedTx = {
31
+ ...unsignedTx,
32
+ data: erc20Interface.encodeFunctionData('transfer', [options.toAddress, amountParsed]),
33
+ value: 0n,
34
+ };
35
+ }
36
+ else {
37
+ if (!options.value)
38
+ throw new Error('Native transfer requires value');
39
+ unsignedTx = {
40
+ to: options.toAddress,
41
+ data: '0x',
42
+ value: (0, ethers_1.parseUnits)(options.value, 18),
43
+ };
44
+ }
45
+ if (options.chainId)
46
+ unsignedTx.chainId = options.chainId;
47
+ const nonce = await provider.getTransactionCount(options.fromAddress, 'pending');
48
+ const estimateGas = await (0, estimateGasLimitFromProvider_1.estimateGasLimitFromProvider)({
49
+ provider: provider,
50
+ unsignedTx,
51
+ walletAddress: options.fromAddress,
52
+ defaultGasLimit: options.defaultGasLimit,
53
+ });
54
+ const unsignedTxToReturn = {
66
55
  from: options.fromAddress,
67
56
  to: unsignedTx.to,
68
57
  data: unsignedTx.data,
69
- value: unsignedTx.value,
70
- gasLimit: estimateGas.gasLimit,
71
- });
58
+ value: unsignedTx.value?.toString(),
59
+ gasLimit: estimateGas.gasLimit.toString(),
60
+ chainId: unsignedTx.chainId,
61
+ nonce,
62
+ maxFeePerGas: estimateGas.feeData.maxFeePerGas?.toString(),
63
+ maxPriorityFeePerGas: estimateGas.feeData.maxPriorityFeePerGas?.toString(),
64
+ };
65
+ if (estimateGas.feeData.gasPrice)
66
+ unsignedTxToReturn.gasPrice = estimateGas.feeData.gasPrice.toString();
67
+ const gasReserve = estimateGas.feeData.maxFeePerGas
68
+ ? estimateGas.gasLimit * estimateGas.feeData.maxFeePerGas
69
+ : estimateGas.feeData.gasPrice
70
+ ? estimateGas.gasLimit * estimateGas.feeData.gasPrice
71
+ : undefined;
72
+ console.log({ gasReserve: gasReserve?.toString() });
73
+ try {
74
+ await provider.call({
75
+ from: options.fromAddress,
76
+ to: unsignedTx.to,
77
+ data: unsignedTx.data,
78
+ value: unsignedTx.value,
79
+ gasLimit: estimateGas.gasLimit,
80
+ });
81
+ }
82
+ catch (error) {
83
+ throw new Error('Transaction would revert, provider call unsuccessful: ' + error.message || error);
84
+ }
85
+ console.log('Built unsigned transfer transaction:', unsignedTxToReturn);
86
+ return {
87
+ unsignedTx: unsignedTxToReturn,
88
+ nonce,
89
+ gasEstimated: estimateGas.gasEstimated.toString(),
90
+ gasLimit: estimateGas.gasLimit.toString(),
91
+ gasReserve: gasReserve?.toString(),
92
+ bufferPercentage: estimateGas.bufferPercentage,
93
+ feeData: {
94
+ maxFeePerGas: estimateGas.feeData.maxFeePerGas?.toString(),
95
+ maxPriorityFeePerGas: estimateGas.feeData.maxPriorityFeePerGas?.toString(),
96
+ gasPrice: estimateGas.feeData.gasPrice?.toString(),
97
+ },
98
+ // suggestedGasFees: estimateGas.suggestedGasFees,
99
+ // humanReadableFees: humanReadableGasEstimation,
100
+ };
72
101
  }
73
102
  catch (error) {
74
- throw new Error('Transaction would revert, provider call unsuccessful: ' + error.message || error);
103
+ console.error('Error building unsigned transfer transaction:', error);
104
+ throw error;
75
105
  }
76
- console.log('Built unsigned transfer transaction:', unsignedTxToReturn);
106
+ };
107
+ exports.buildUnsignedTransferTx = buildUnsignedTransferTx;
108
+ const buildMaxNativeTransferTx = async (options) => {
109
+ const isNativeToken = !options.tokenAddress ||
110
+ options.tokenAddress?.toLowerCase() === '0x0000000000000000000000000000000000000000' ||
111
+ options.tokenAddress?.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
112
+ if (!isNativeToken)
113
+ throw new Error('Max native transfer requires a native token address');
114
+ const provisionalTx = await (0, exports.buildUnsignedTransferTx)({
115
+ ...options,
116
+ value: '0',
117
+ tokenAddress: options.tokenAddress,
118
+ });
119
+ if (!provisionalTx.gasReserve)
120
+ throw new Error('Could not determine gas reserve for max send');
121
+ const balanceWei = (0, ethers_1.parseUnits)(options.balance, 18);
122
+ const gasReserveWei = BigInt(provisionalTx.gasReserve);
123
+ const sendableWei = balanceWei - gasReserveWei;
124
+ if (sendableWei <= 0n)
125
+ throw new Error('Insufficient balance to cover gas reserve');
126
+ const sendableValue = (0, ethers_1.formatUnits)(sendableWei, 18);
127
+ const finalTx = await (0, exports.buildUnsignedTransferTx)({
128
+ ...options,
129
+ value: sendableValue,
130
+ tokenAddress: options.tokenAddress,
131
+ });
77
132
  return {
78
- unsignedTx: unsignedTxToReturn,
79
- nonce,
80
- gasEstimated: estimateGas.gasEstimated.toString(),
81
- gasLimit: estimateGas.gasLimit.toString(),
82
- bufferPercentage: estimateGas.bufferPercentage,
83
- feeData: {
84
- maxFeePerGas: estimateGas.feeData.maxFeePerGas?.toString(),
85
- maxPriorityFeePerGas: estimateGas.feeData.maxPriorityFeePerGas?.toString(),
86
- gasPrice: estimateGas.feeData.gasPrice?.toString(),
87
- },
88
- // suggestedGasFees: estimateGas.suggestedGasFees,
89
- // humanReadableFees: humanReadableGasEstimation,
133
+ ...finalTx,
134
+ sendableValue,
90
135
  };
91
136
  };
92
- exports.buildUnsignedTransferTx = buildUnsignedTransferTx;
137
+ exports.buildMaxNativeTransferTx = buildMaxNativeTransferTx;
@@ -2,8 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.estimateGasLimitFromProvider = void 0;
4
4
  const estimateGasLimitFromProvider = async ({ provider, unsignedTx, walletAddress, defaultGasLimit, }) => {
5
+ let lastFeeData = null;
6
+ let lastGasEstimated = null;
5
7
  try {
6
8
  const feeData = await provider.getFeeData();
9
+ lastFeeData = feeData;
7
10
  const txForEstimation = { ...unsignedTx, from: walletAddress };
8
11
  if (feeData.maxFeePerGas !== undefined)
9
12
  txForEstimation.maxFeePerGas = feeData.maxFeePerGas;
@@ -11,7 +14,9 @@ const estimateGasLimitFromProvider = async ({ provider, unsignedTx, walletAddres
11
14
  txForEstimation.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;
12
15
  if (feeData.gasPrice !== undefined && txForEstimation.maxFeePerGas === undefined)
13
16
  txForEstimation.gasPrice = feeData.gasPrice;
17
+ console.log('Estimating gas with transaction:!!!!!! ', txForEstimation);
14
18
  const gasEstimated = await provider.estimateGas(txForEstimation);
19
+ lastGasEstimated = gasEstimated;
15
20
  let congestionFactor = 1.5;
16
21
  if (feeData.maxFeePerGas && feeData.maxPriorityFeePerGas) {
17
22
  try {
@@ -43,12 +48,20 @@ const estimateGasLimitFromProvider = async ({ provider, unsignedTx, walletAddres
43
48
  catch (error) {
44
49
  console.error('Unable to estimate gas limit: ', error);
45
50
  console.log(`Setting default gas limit to: ${defaultGasLimit}`);
51
+ const feeData = lastFeeData;
52
+ const gasEstimated = lastGasEstimated ?? defaultGasLimit;
46
53
  return {
47
- gasEstimated: defaultGasLimit,
54
+ gasEstimated,
48
55
  gasLimit: defaultGasLimit,
49
56
  bufferPercentage: 0,
50
57
  fallbackUsed: true,
51
- feeData: {},
58
+ feeData: feeData
59
+ ? {
60
+ maxFeePerGas: feeData.maxFeePerGas ?? undefined,
61
+ maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined,
62
+ gasPrice: feeData.gasPrice ?? undefined,
63
+ }
64
+ : {},
52
65
  };
53
66
  }
54
67
  };
@@ -1,3 +1,3 @@
1
1
  /** npm imports */
2
2
  import { JsonRpcProvider } from 'ethers';
3
- export declare const getProvider: (rpcUrl: string, chainId?: number) => JsonRpcProvider;
3
+ export declare const getProvider: (rpcUrl: string, chainId?: number) => JsonRpcProvider | undefined;
@@ -4,8 +4,17 @@ exports.getProvider = void 0;
4
4
  /** npm imports */
5
5
  const ethers_1 = require("ethers");
6
6
  const getProvider = (rpcUrl, chainId) => {
7
- const provider = new ethers_1.JsonRpcProvider(rpcUrl, { chainId });
8
- console.log({ provider });
9
- return provider;
7
+ try {
8
+ if (chainId) {
9
+ const network = ethers_1.Network.from(chainId);
10
+ return new ethers_1.JsonRpcProvider(rpcUrl, network, { staticNetwork: network });
11
+ }
12
+ else {
13
+ return new ethers_1.JsonRpcProvider(rpcUrl);
14
+ }
15
+ }
16
+ catch (error) {
17
+ console.warn('Could not create provider with chainId, falling back to rpcUrl only:', error);
18
+ }
10
19
  };
11
20
  exports.getProvider = getProvider;
@@ -5,6 +5,8 @@ exports.txStatus = void 0;
5
5
  const getProvider_1 = require("./getProvider");
6
6
  const txStatus = async ({ rpcUrl, txHash, chainId, }) => {
7
7
  const provider = (0, getProvider_1.getProvider)(rpcUrl, chainId);
8
+ if (!provider)
9
+ throw new Error('Could not create provider with given rpcUrl and chainId');
8
10
  try {
9
11
  const receipt = await provider.getTransactionReceipt(txHash);
10
12
  if (!receipt)
@@ -4,15 +4,20 @@ export { ERC20_TOKEN_CONTRACT_ABI };
4
4
  /** constants exports */
5
5
  export { GAS_LIMIT_PER_TX_TYPE } from './constants/constants';
6
6
  /** basic blockchain exports */
7
- export { buildUnsignedTransferTx } from './blockchain/buildUnsignedTransferTx';
7
+ export { buildMaxNativeTransferTx, buildUnsignedTransferTx, } from './blockchain/buildUnsignedTransferTx';
8
8
  export { broadcastTransaction } from './blockchain/broadcastTransaction';
9
9
  export { estimateGasLimitFromProvider } from './blockchain/estimateGasLimitFromProvider';
10
10
  export { getProvider } from './blockchain/getProvider';
11
11
  export { txStatus } from './blockchain/txStatus';
12
+ /** services exports */
13
+ export { EvmWalletService, EvmGeneratedWallet, EvmDerivedWallet, } from './services/evm-wallet-core/evmWalletService';
14
+ export { EntropySource } from './services/evm-wallet-core/entropy';
15
+ export { createWallet, signMessage, signTransaction } from './services/evm-wallet-core/signer';
12
16
  /** utils exports */
13
17
  export { getShortenTransactionHash } from './utils/getShortenTxHash';
14
18
  export { transformBigInt } from './utils/transformBigInt';
15
19
  import NATIVE_TOKENS from './utils/tokens';
16
20
  export { NATIVE_TOKENS };
21
+ export { formattedAmountForDisplay, parsedAmount } from './utils/formatAmount';
17
22
  /** types exports */
18
- export { BroadcastTransactionOptions, BuildUnsignedTransferTxOptions, EstimateGasLimitFromProviderProps, GasEstimateResult, TxStatusOptions, TxStatusResponse, UnsignedTransferTxResponse, } from './types/common';
23
+ export { BroadcastTransactionOptions, BuildMaxNativeTransferTxOptions, BuildMaxNativeTransferTxResponse, BuildUnsignedTransferTxOptions, EstimateGasLimitFromProviderProps, GasEstimateResult, TxStatusOptions, TxStatusResponse, UnsignedTransferTxResponse, FormatAmountOptions, TransactionRequest, } from './types/common';
package/dist/cjs/index.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.NATIVE_TOKENS = exports.transformBigInt = exports.getShortenTransactionHash = exports.txStatus = exports.getProvider = exports.estimateGasLimitFromProvider = exports.broadcastTransaction = exports.buildUnsignedTransferTx = exports.GAS_LIMIT_PER_TX_TYPE = exports.ERC20_TOKEN_CONTRACT_ABI = void 0;
6
+ exports.parsedAmount = exports.formattedAmountForDisplay = exports.NATIVE_TOKENS = exports.transformBigInt = exports.getShortenTransactionHash = exports.signTransaction = exports.signMessage = exports.createWallet = exports.EvmWalletService = exports.txStatus = exports.getProvider = exports.estimateGasLimitFromProvider = exports.broadcastTransaction = exports.buildUnsignedTransferTx = exports.buildMaxNativeTransferTx = exports.GAS_LIMIT_PER_TX_TYPE = exports.ERC20_TOKEN_CONTRACT_ABI = void 0;
7
7
  /** ABIs exports */
8
8
  const ERC20_TOKEN_CONTRACT_ABI_1 = __importDefault(require("./ABIs/ERC20_TOKEN_CONTRACT_ABI"));
9
9
  exports.ERC20_TOKEN_CONTRACT_ABI = ERC20_TOKEN_CONTRACT_ABI_1.default;
@@ -12,6 +12,7 @@ var constants_1 = require("./constants/constants");
12
12
  Object.defineProperty(exports, "GAS_LIMIT_PER_TX_TYPE", { enumerable: true, get: function () { return constants_1.GAS_LIMIT_PER_TX_TYPE; } });
13
13
  /** basic blockchain exports */
14
14
  var buildUnsignedTransferTx_1 = require("./blockchain/buildUnsignedTransferTx");
15
+ Object.defineProperty(exports, "buildMaxNativeTransferTx", { enumerable: true, get: function () { return buildUnsignedTransferTx_1.buildMaxNativeTransferTx; } });
15
16
  Object.defineProperty(exports, "buildUnsignedTransferTx", { enumerable: true, get: function () { return buildUnsignedTransferTx_1.buildUnsignedTransferTx; } });
16
17
  var broadcastTransaction_1 = require("./blockchain/broadcastTransaction");
17
18
  Object.defineProperty(exports, "broadcastTransaction", { enumerable: true, get: function () { return broadcastTransaction_1.broadcastTransaction; } });
@@ -21,6 +22,13 @@ var getProvider_1 = require("./blockchain/getProvider");
21
22
  Object.defineProperty(exports, "getProvider", { enumerable: true, get: function () { return getProvider_1.getProvider; } });
22
23
  var txStatus_1 = require("./blockchain/txStatus");
23
24
  Object.defineProperty(exports, "txStatus", { enumerable: true, get: function () { return txStatus_1.txStatus; } });
25
+ /** services exports */
26
+ var evmWalletService_1 = require("./services/evm-wallet-core/evmWalletService");
27
+ Object.defineProperty(exports, "EvmWalletService", { enumerable: true, get: function () { return evmWalletService_1.EvmWalletService; } });
28
+ var signer_1 = require("./services/evm-wallet-core/signer");
29
+ Object.defineProperty(exports, "createWallet", { enumerable: true, get: function () { return signer_1.createWallet; } });
30
+ Object.defineProperty(exports, "signMessage", { enumerable: true, get: function () { return signer_1.signMessage; } });
31
+ Object.defineProperty(exports, "signTransaction", { enumerable: true, get: function () { return signer_1.signTransaction; } });
24
32
  /** utils exports */
25
33
  var getShortenTxHash_1 = require("./utils/getShortenTxHash");
26
34
  Object.defineProperty(exports, "getShortenTransactionHash", { enumerable: true, get: function () { return getShortenTxHash_1.getShortenTransactionHash; } });
@@ -28,3 +36,6 @@ var transformBigInt_1 = require("./utils/transformBigInt");
28
36
  Object.defineProperty(exports, "transformBigInt", { enumerable: true, get: function () { return transformBigInt_1.transformBigInt; } });
29
37
  const tokens_1 = __importDefault(require("./utils/tokens"));
30
38
  exports.NATIVE_TOKENS = tokens_1.default;
39
+ var formatAmount_1 = require("./utils/formatAmount");
40
+ Object.defineProperty(exports, "formattedAmountForDisplay", { enumerable: true, get: function () { return formatAmount_1.formattedAmountForDisplay; } });
41
+ Object.defineProperty(exports, "parsedAmount", { enumerable: true, get: function () { return formatAmount_1.parsedAmount; } });
@@ -0,0 +1,3 @@
1
+ export interface EntropySource {
2
+ randomBytes(length: number): Uint8Array;
3
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,27 @@
1
+ /** local imports */
2
+ import { EntropySource } from './entropy';
3
+ export interface EvmGeneratedWallet {
4
+ address: string;
5
+ mnemonic: string;
6
+ privateKey: string;
7
+ path: string;
8
+ }
9
+ export interface EvmDerivedWallet {
10
+ address: string;
11
+ privateKey: string;
12
+ path: string;
13
+ publicKey: string;
14
+ }
15
+ export declare class EvmWalletService {
16
+ private entropy;
17
+ private readonly DEFAULT_DERIVATION_PATH;
18
+ constructor(entropy: EntropySource);
19
+ generateNewWallet(accountIndex?: number, wordCount?: 12 | 15 | 18 | 21 | 24): EvmGeneratedWallet;
20
+ generateMultipleWallets(mnemonic: string, count: number, startIndex?: number): EvmDerivedWallet[];
21
+ deriveWalletFromMnemonic(mnemonic: string, accountIndex?: number, customPath?: string): EvmDerivedWallet;
22
+ deriveFromPrivateKey(privateKey: string): Omit<EvmDerivedWallet, 'path'>;
23
+ signMessage(privateKey: string, message: string): Promise<string>;
24
+ isValidMnemonic(mnemonic: string): boolean;
25
+ private validateMnemonic;
26
+ private getEntropyForWordCount;
27
+ }
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EvmWalletService = void 0;
4
+ /** npm imports */
5
+ const ethers_1 = require("ethers");
6
+ class EvmWalletService {
7
+ constructor(entropy) {
8
+ this.entropy = entropy;
9
+ this.DEFAULT_DERIVATION_PATH = "m/44'/60'/0'/0";
10
+ }
11
+ generateNewWallet(accountIndex = 0, wordCount = 12) {
12
+ const entropy = this.getEntropyForWordCount(wordCount);
13
+ const randomBytesArray = this.entropy.randomBytes(entropy);
14
+ const customMnemonic = ethers_1.Mnemonic.fromEntropy(randomBytesArray);
15
+ const path = `${this.DEFAULT_DERIVATION_PATH}/${accountIndex}`;
16
+ const hdNode = ethers_1.HDNodeWallet.fromMnemonic(customMnemonic, path);
17
+ return {
18
+ address: hdNode.address,
19
+ mnemonic: customMnemonic.phrase,
20
+ privateKey: hdNode.privateKey,
21
+ path,
22
+ };
23
+ }
24
+ generateMultipleWallets(mnemonic, count, startIndex = 0) {
25
+ this.validateMnemonic(mnemonic);
26
+ const wallets = [];
27
+ const mnemonicObject = ethers_1.Mnemonic.fromPhrase(mnemonic);
28
+ for (let i = 0; i < count; i++) {
29
+ const accountIndex = startIndex + i;
30
+ const path = `${this.DEFAULT_DERIVATION_PATH}/${accountIndex}`;
31
+ const hdNode = ethers_1.HDNodeWallet.fromMnemonic(mnemonicObject, path);
32
+ wallets.push({
33
+ address: hdNode.address,
34
+ privateKey: hdNode.privateKey,
35
+ path,
36
+ publicKey: hdNode.publicKey,
37
+ });
38
+ }
39
+ return wallets;
40
+ }
41
+ deriveWalletFromMnemonic(mnemonic, accountIndex = 0, customPath) {
42
+ this.validateMnemonic(mnemonic);
43
+ const path = customPath || `${this.DEFAULT_DERIVATION_PATH}/${accountIndex}`;
44
+ const mnemonicObject = ethers_1.Mnemonic.fromPhrase(mnemonic);
45
+ const hdNode = ethers_1.HDNodeWallet.fromMnemonic(mnemonicObject, path);
46
+ return {
47
+ address: hdNode.address,
48
+ privateKey: hdNode.privateKey,
49
+ path,
50
+ publicKey: hdNode.publicKey,
51
+ };
52
+ }
53
+ deriveFromPrivateKey(privateKey) {
54
+ const wallet = new ethers_1.Wallet(privateKey);
55
+ return {
56
+ address: wallet.address,
57
+ privateKey: wallet.privateKey,
58
+ publicKey: wallet.signingKey.publicKey,
59
+ };
60
+ }
61
+ async signMessage(privateKey, message) {
62
+ const wallet = new ethers_1.Wallet(privateKey);
63
+ return await wallet.signMessage(message);
64
+ }
65
+ isValidMnemonic(mnemonic) {
66
+ try {
67
+ ethers_1.Mnemonic.fromPhrase(mnemonic);
68
+ return true;
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ }
74
+ validateMnemonic(mnemonic) {
75
+ if (!this.isValidMnemonic(mnemonic)) {
76
+ throw new Error('Invalid mnemonic phrase');
77
+ }
78
+ }
79
+ getEntropyForWordCount(wordCount) {
80
+ const entropyMap = {
81
+ 12: 16, // 128 bits
82
+ 15: 20, // 160 bits
83
+ 18: 24, // 192 bits
84
+ 21: 28, // 224 bits
85
+ 24: 32, // 256 bits
86
+ };
87
+ return entropyMap[wordCount];
88
+ }
89
+ }
90
+ exports.EvmWalletService = EvmWalletService;
@@ -0,0 +1,7 @@
1
+ /** npm imports */
2
+ import { Wallet } from 'ethers';
3
+ /** local imports */
4
+ import { TransactionRequest } from '../../types/common';
5
+ export declare const createWallet: (privateKey: string, rpcUrl?: string) => Wallet;
6
+ export declare const signMessage: (privateKey: string, message: string) => Promise<string>;
7
+ export declare const signTransaction: (privateKey: string, tx: TransactionRequest, rpcUrl?: string) => Promise<string>;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.signTransaction = exports.signMessage = exports.createWallet = void 0;
4
+ /** npm imports */
5
+ const ethers_1 = require("ethers");
6
+ const createWallet = (privateKey, rpcUrl) => new ethers_1.Wallet(privateKey, rpcUrl ? new ethers_1.JsonRpcProvider(rpcUrl) : undefined);
7
+ exports.createWallet = createWallet;
8
+ const signMessage = async (privateKey, message) => {
9
+ const wallet = (0, exports.createWallet)(privateKey);
10
+ return wallet.signMessage(message);
11
+ };
12
+ exports.signMessage = signMessage;
13
+ const signTransaction = async (privateKey, tx, rpcUrl) => {
14
+ const wallet = (0, exports.createWallet)(privateKey, rpcUrl);
15
+ return wallet.signTransaction(tx);
16
+ };
17
+ exports.signTransaction = signTransaction;
@@ -33,6 +33,7 @@ export interface UnsignedTransferTxResponse {
33
33
  nonce: number;
34
34
  gasEstimated: string;
35
35
  gasLimit: string;
36
+ gasReserve?: string;
36
37
  bufferPercentage: number;
37
38
  feeData: {
38
39
  maxFeePerGas?: string;
@@ -40,9 +41,17 @@ export interface UnsignedTransferTxResponse {
40
41
  gasPrice?: string;
41
42
  };
42
43
  }
44
+ export interface BuildMaxNativeTransferTxOptions extends Omit<BuildUnsignedTransferTxOptions, 'value' | 'tokenAddress' | 'tokenDecimals'> {
45
+ balance: string;
46
+ tokenAddress?: string;
47
+ }
48
+ export interface BuildMaxNativeTransferTxResponse extends UnsignedTransferTxResponse {
49
+ sendableValue: string;
50
+ }
43
51
  export interface BroadcastTransactionOptions {
44
52
  signedTx: string;
45
53
  rpcUrl: string;
54
+ chainId?: number;
46
55
  waitConfirmations?: number;
47
56
  }
48
57
  export interface TxStatusOptions {
@@ -54,3 +63,12 @@ export interface TxStatusResponse {
54
63
  success: boolean;
55
64
  receipt: TransactionReceipt | null;
56
65
  }
66
+ export interface FormatAmountOptions {
67
+ decimalsToShow?: number;
68
+ useGroupSeparator?: boolean;
69
+ locale?: string;
70
+ minimumFractionDigits?: number;
71
+ minDisplayDecimals?: number;
72
+ maxDisplayDigits?: number;
73
+ }
74
+ export type { TransactionRequest };
@@ -0,0 +1,6 @@
1
+ /** npm imports */
2
+ import { BigNumberish } from 'ethers';
3
+ /** local imports */
4
+ import { FormatAmountOptions } from '../types/common';
5
+ export declare const formattedAmountForDisplay: (amount: BigNumberish, decimals: number, options?: FormatAmountOptions) => string;
6
+ export declare const parsedAmount: (amount: string, decimals: number) => bigint;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parsedAmount = exports.formattedAmountForDisplay = void 0;
4
+ /** npm imports */
5
+ const ethers_1 = require("ethers");
6
+ const formattedAmountForDisplay = (amount, decimals, options = {}) => {
7
+ const { decimalsToShow = 0, useGroupSeparator = true, locale = 'en-US', minimumFractionDigits, minDisplayDecimals = 9, maxDisplayDigits = 16, } = options;
8
+ const formattedAmount = (0, ethers_1.formatUnits)(amount, decimals);
9
+ if (formattedAmount.startsWith('-')) {
10
+ throw new Error('Negative balances are not supported');
11
+ }
12
+ const [wholePart = '0', fractionPart = ''] = formattedAmount.split('.');
13
+ if (wholePart.length >= maxDisplayDigits) {
14
+ const mantissaWhole = wholePart[0] ?? '0';
15
+ const mantissaFraction = (wholePart.slice(1) + fractionPart).slice(0, decimalsToShow);
16
+ const exponent = wholePart.length - 1;
17
+ const rawMantissa = mantissaFraction ? `${mantissaWhole}.${mantissaFraction}` : mantissaWhole;
18
+ const trimmedMantissa = rawMantissa.replace(/\.0+$|(?<=\.[0-9]*?)0+$/g, '').replace(/\.$/, '');
19
+ return `${trimmedMantissa}e${exponent}`;
20
+ }
21
+ const firstNonZeroIndex = fractionPart.split('').findIndex((digit) => digit !== '0');
22
+ const hasNonZeroFraction = firstNonZeroIndex !== -1;
23
+ if (hasNonZeroFraction && wholePart === '0' && firstNonZeroIndex >= minDisplayDecimals) {
24
+ const sample = new Intl.NumberFormat(locale).format(1000.1);
25
+ const decimalSeparator = sample.replace(/\d/g, '').slice(-1) ?? '.';
26
+ const minValue = `0${decimalSeparator}${'0'.repeat(Math.max(0, minDisplayDecimals - 1))}1`;
27
+ return `< ${minValue}`;
28
+ }
29
+ if (!hasNonZeroFraction) {
30
+ const sample = new Intl.NumberFormat(locale).format(1000.1);
31
+ const groupSeparator = sample.replace(/\d/g, '')[0] ?? ',';
32
+ const groupedWhole = useGroupSeparator
33
+ ? (wholePart || '0').replace(/\B(?=(\d{3})+(?!\d))/g, groupSeparator)
34
+ : wholePart;
35
+ return groupedWhole;
36
+ }
37
+ const maxDecimals = decimalsToShow ?? fractionPart.length;
38
+ let trimmedFraction = fractionPart.slice(0, Math.max(0, maxDecimals));
39
+ const minDecimals = minimumFractionDigits ?? 0;
40
+ if (trimmedFraction.length < minDecimals) {
41
+ trimmedFraction = trimmedFraction.padEnd(minDecimals, '0');
42
+ }
43
+ const sample = new Intl.NumberFormat(locale).format(1000.1);
44
+ const groupSeparator = sample.replace(/\d/g, '')[0] ?? ',';
45
+ const decimalSeparator = sample.replace(/\d/g, '').slice(-1) ?? '.';
46
+ const groupedWhole = useGroupSeparator
47
+ ? (wholePart || '0').replace(/\B(?=(\d{3})+(?!\d))/g, groupSeparator)
48
+ : wholePart;
49
+ if (!trimmedFraction)
50
+ return groupedWhole;
51
+ return `${groupedWhole}${decimalSeparator}${trimmedFraction}`;
52
+ };
53
+ exports.formattedAmountForDisplay = formattedAmountForDisplay;
54
+ const parsedAmount = (amount, decimals) => (0, ethers_1.parseUnits)(amount, decimals);
55
+ exports.parsedAmount = parsedAmount;
package/dist/index.d.ts CHANGED
@@ -4,15 +4,20 @@ export { ERC20_TOKEN_CONTRACT_ABI };
4
4
  /** constants exports */
5
5
  export { GAS_LIMIT_PER_TX_TYPE } from './constants/constants';
6
6
  /** basic blockchain exports */
7
- export { buildUnsignedTransferTx } from './blockchain/buildUnsignedTransferTx';
7
+ export { buildMaxNativeTransferTx, buildUnsignedTransferTx, } from './blockchain/buildUnsignedTransferTx';
8
8
  export { broadcastTransaction } from './blockchain/broadcastTransaction';
9
9
  export { estimateGasLimitFromProvider } from './blockchain/estimateGasLimitFromProvider';
10
10
  export { getProvider } from './blockchain/getProvider';
11
11
  export { txStatus } from './blockchain/txStatus';
12
+ /** services exports */
13
+ export { EvmWalletService, EvmGeneratedWallet, EvmDerivedWallet, } from './services/evm-wallet-core/evmWalletService';
14
+ export { EntropySource } from './services/evm-wallet-core/entropy';
15
+ export { createWallet, signMessage, signTransaction } from './services/evm-wallet-core/signer';
12
16
  /** utils exports */
13
17
  export { getShortenTransactionHash } from './utils/getShortenTxHash';
14
18
  export { transformBigInt } from './utils/transformBigInt';
15
19
  import NATIVE_TOKENS from './utils/tokens';
16
20
  export { NATIVE_TOKENS };
21
+ export { formattedAmountForDisplay, parsedAmount } from './utils/formatAmount';
17
22
  /** types exports */
18
- export { BroadcastTransactionOptions, BuildUnsignedTransferTxOptions, EstimateGasLimitFromProviderProps, GasEstimateResult, TxStatusOptions, TxStatusResponse, UnsignedTransferTxResponse, } from './types/common';
23
+ export { BroadcastTransactionOptions, BuildMaxNativeTransferTxOptions, BuildMaxNativeTransferTxResponse, BuildUnsignedTransferTxOptions, EstimateGasLimitFromProviderProps, GasEstimateResult, TxStatusOptions, TxStatusResponse, UnsignedTransferTxResponse, FormatAmountOptions, TransactionRequest, } from './types/common';
package/dist/index.js CHANGED
@@ -4,13 +4,17 @@ export { ERC20_TOKEN_CONTRACT_ABI };
4
4
  /** constants exports */
5
5
  export { GAS_LIMIT_PER_TX_TYPE } from './constants/constants';
6
6
  /** basic blockchain exports */
7
- export { buildUnsignedTransferTx } from './blockchain/buildUnsignedTransferTx';
7
+ export { buildMaxNativeTransferTx, buildUnsignedTransferTx, } from './blockchain/buildUnsignedTransferTx';
8
8
  export { broadcastTransaction } from './blockchain/broadcastTransaction';
9
9
  export { estimateGasLimitFromProvider } from './blockchain/estimateGasLimitFromProvider';
10
10
  export { getProvider } from './blockchain/getProvider';
11
11
  export { txStatus } from './blockchain/txStatus';
12
+ /** services exports */
13
+ export { EvmWalletService, } from './services/evm-wallet-core/evmWalletService';
14
+ export { createWallet, signMessage, signTransaction } from './services/evm-wallet-core/signer';
12
15
  /** utils exports */
13
16
  export { getShortenTransactionHash } from './utils/getShortenTxHash';
14
17
  export { transformBigInt } from './utils/transformBigInt';
15
18
  import NATIVE_TOKENS from './utils/tokens';
16
19
  export { NATIVE_TOKENS };
20
+ export { formattedAmountForDisplay, parsedAmount } from './utils/formatAmount';
@@ -0,0 +1,3 @@
1
+ export interface EntropySource {
2
+ randomBytes(length: number): Uint8Array;
3
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ /** local imports */
2
+ import { EntropySource } from './entropy';
3
+ export interface EvmGeneratedWallet {
4
+ address: string;
5
+ mnemonic: string;
6
+ privateKey: string;
7
+ path: string;
8
+ }
9
+ export interface EvmDerivedWallet {
10
+ address: string;
11
+ privateKey: string;
12
+ path: string;
13
+ publicKey: string;
14
+ }
15
+ export declare class EvmWalletService {
16
+ private entropy;
17
+ private readonly DEFAULT_DERIVATION_PATH;
18
+ constructor(entropy: EntropySource);
19
+ generateNewWallet(accountIndex?: number, wordCount?: 12 | 15 | 18 | 21 | 24): EvmGeneratedWallet;
20
+ generateMultipleWallets(mnemonic: string, count: number, startIndex?: number): EvmDerivedWallet[];
21
+ deriveWalletFromMnemonic(mnemonic: string, accountIndex?: number, customPath?: string): EvmDerivedWallet;
22
+ deriveFromPrivateKey(privateKey: string): Omit<EvmDerivedWallet, 'path'>;
23
+ signMessage(privateKey: string, message: string): Promise<string>;
24
+ isValidMnemonic(mnemonic: string): boolean;
25
+ private validateMnemonic;
26
+ private getEntropyForWordCount;
27
+ }
@@ -0,0 +1,86 @@
1
+ /** npm imports */
2
+ import { Wallet, HDNodeWallet, Mnemonic } from 'ethers';
3
+ export class EvmWalletService {
4
+ constructor(entropy) {
5
+ this.entropy = entropy;
6
+ this.DEFAULT_DERIVATION_PATH = "m/44'/60'/0'/0";
7
+ }
8
+ generateNewWallet(accountIndex = 0, wordCount = 12) {
9
+ const entropy = this.getEntropyForWordCount(wordCount);
10
+ const randomBytesArray = this.entropy.randomBytes(entropy);
11
+ const customMnemonic = Mnemonic.fromEntropy(randomBytesArray);
12
+ const path = `${this.DEFAULT_DERIVATION_PATH}/${accountIndex}`;
13
+ const hdNode = HDNodeWallet.fromMnemonic(customMnemonic, path);
14
+ return {
15
+ address: hdNode.address,
16
+ mnemonic: customMnemonic.phrase,
17
+ privateKey: hdNode.privateKey,
18
+ path,
19
+ };
20
+ }
21
+ generateMultipleWallets(mnemonic, count, startIndex = 0) {
22
+ this.validateMnemonic(mnemonic);
23
+ const wallets = [];
24
+ const mnemonicObject = Mnemonic.fromPhrase(mnemonic);
25
+ for (let i = 0; i < count; i++) {
26
+ const accountIndex = startIndex + i;
27
+ const path = `${this.DEFAULT_DERIVATION_PATH}/${accountIndex}`;
28
+ const hdNode = HDNodeWallet.fromMnemonic(mnemonicObject, path);
29
+ wallets.push({
30
+ address: hdNode.address,
31
+ privateKey: hdNode.privateKey,
32
+ path,
33
+ publicKey: hdNode.publicKey,
34
+ });
35
+ }
36
+ return wallets;
37
+ }
38
+ deriveWalletFromMnemonic(mnemonic, accountIndex = 0, customPath) {
39
+ this.validateMnemonic(mnemonic);
40
+ const path = customPath || `${this.DEFAULT_DERIVATION_PATH}/${accountIndex}`;
41
+ const mnemonicObject = Mnemonic.fromPhrase(mnemonic);
42
+ const hdNode = HDNodeWallet.fromMnemonic(mnemonicObject, path);
43
+ return {
44
+ address: hdNode.address,
45
+ privateKey: hdNode.privateKey,
46
+ path,
47
+ publicKey: hdNode.publicKey,
48
+ };
49
+ }
50
+ deriveFromPrivateKey(privateKey) {
51
+ const wallet = new Wallet(privateKey);
52
+ return {
53
+ address: wallet.address,
54
+ privateKey: wallet.privateKey,
55
+ publicKey: wallet.signingKey.publicKey,
56
+ };
57
+ }
58
+ async signMessage(privateKey, message) {
59
+ const wallet = new Wallet(privateKey);
60
+ return await wallet.signMessage(message);
61
+ }
62
+ isValidMnemonic(mnemonic) {
63
+ try {
64
+ Mnemonic.fromPhrase(mnemonic);
65
+ return true;
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ }
71
+ validateMnemonic(mnemonic) {
72
+ if (!this.isValidMnemonic(mnemonic)) {
73
+ throw new Error('Invalid mnemonic phrase');
74
+ }
75
+ }
76
+ getEntropyForWordCount(wordCount) {
77
+ const entropyMap = {
78
+ 12: 16, // 128 bits
79
+ 15: 20, // 160 bits
80
+ 18: 24, // 192 bits
81
+ 21: 28, // 224 bits
82
+ 24: 32, // 256 bits
83
+ };
84
+ return entropyMap[wordCount];
85
+ }
86
+ }
@@ -0,0 +1,7 @@
1
+ /** npm imports */
2
+ import { Wallet } from 'ethers';
3
+ /** local imports */
4
+ import { TransactionRequest } from '../../types/common';
5
+ export declare const createWallet: (privateKey: string, rpcUrl?: string) => Wallet;
6
+ export declare const signMessage: (privateKey: string, message: string) => Promise<string>;
7
+ export declare const signTransaction: (privateKey: string, tx: TransactionRequest, rpcUrl?: string) => Promise<string>;
@@ -0,0 +1,11 @@
1
+ /** npm imports */
2
+ import { Wallet, JsonRpcProvider } from 'ethers';
3
+ export const createWallet = (privateKey, rpcUrl) => new Wallet(privateKey, rpcUrl ? new JsonRpcProvider(rpcUrl) : undefined);
4
+ export const signMessage = async (privateKey, message) => {
5
+ const wallet = createWallet(privateKey);
6
+ return wallet.signMessage(message);
7
+ };
8
+ export const signTransaction = async (privateKey, tx, rpcUrl) => {
9
+ const wallet = createWallet(privateKey, rpcUrl);
10
+ return wallet.signTransaction(tx);
11
+ };
@@ -33,6 +33,7 @@ export interface UnsignedTransferTxResponse {
33
33
  nonce: number;
34
34
  gasEstimated: string;
35
35
  gasLimit: string;
36
+ gasReserve?: string;
36
37
  bufferPercentage: number;
37
38
  feeData: {
38
39
  maxFeePerGas?: string;
@@ -40,9 +41,17 @@ export interface UnsignedTransferTxResponse {
40
41
  gasPrice?: string;
41
42
  };
42
43
  }
44
+ export interface BuildMaxNativeTransferTxOptions extends Omit<BuildUnsignedTransferTxOptions, 'value' | 'tokenAddress' | 'tokenDecimals'> {
45
+ balance: string;
46
+ tokenAddress?: string;
47
+ }
48
+ export interface BuildMaxNativeTransferTxResponse extends UnsignedTransferTxResponse {
49
+ sendableValue: string;
50
+ }
43
51
  export interface BroadcastTransactionOptions {
44
52
  signedTx: string;
45
53
  rpcUrl: string;
54
+ chainId?: number;
46
55
  waitConfirmations?: number;
47
56
  }
48
57
  export interface TxStatusOptions {
@@ -54,3 +63,12 @@ export interface TxStatusResponse {
54
63
  success: boolean;
55
64
  receipt: TransactionReceipt | null;
56
65
  }
66
+ export interface FormatAmountOptions {
67
+ decimalsToShow?: number;
68
+ useGroupSeparator?: boolean;
69
+ locale?: string;
70
+ minimumFractionDigits?: number;
71
+ minDisplayDecimals?: number;
72
+ maxDisplayDigits?: number;
73
+ }
74
+ export type { TransactionRequest };
@@ -0,0 +1,6 @@
1
+ /** npm imports */
2
+ import { BigNumberish } from 'ethers';
3
+ /** local imports */
4
+ import { FormatAmountOptions } from '../types/common';
5
+ export declare const formattedAmountForDisplay: (amount: BigNumberish, decimals: number, options?: FormatAmountOptions) => string;
6
+ export declare const parsedAmount: (amount: string, decimals: number) => bigint;
@@ -0,0 +1,50 @@
1
+ /** npm imports */
2
+ import { formatUnits, parseUnits } from 'ethers';
3
+ export const formattedAmountForDisplay = (amount, decimals, options = {}) => {
4
+ const { decimalsToShow = 0, useGroupSeparator = true, locale = 'en-US', minimumFractionDigits, minDisplayDecimals = 9, maxDisplayDigits = 16, } = options;
5
+ const formattedAmount = formatUnits(amount, decimals);
6
+ if (formattedAmount.startsWith('-')) {
7
+ throw new Error('Negative balances are not supported');
8
+ }
9
+ const [wholePart = '0', fractionPart = ''] = formattedAmount.split('.');
10
+ if (wholePart.length >= maxDisplayDigits) {
11
+ const mantissaWhole = wholePart[0] ?? '0';
12
+ const mantissaFraction = (wholePart.slice(1) + fractionPart).slice(0, decimalsToShow);
13
+ const exponent = wholePart.length - 1;
14
+ const rawMantissa = mantissaFraction ? `${mantissaWhole}.${mantissaFraction}` : mantissaWhole;
15
+ const trimmedMantissa = rawMantissa.replace(/\.0+$|(?<=\.[0-9]*?)0+$/g, '').replace(/\.$/, '');
16
+ return `${trimmedMantissa}e${exponent}`;
17
+ }
18
+ const firstNonZeroIndex = fractionPart.split('').findIndex((digit) => digit !== '0');
19
+ const hasNonZeroFraction = firstNonZeroIndex !== -1;
20
+ if (hasNonZeroFraction && wholePart === '0' && firstNonZeroIndex >= minDisplayDecimals) {
21
+ const sample = new Intl.NumberFormat(locale).format(1000.1);
22
+ const decimalSeparator = sample.replace(/\d/g, '').slice(-1) ?? '.';
23
+ const minValue = `0${decimalSeparator}${'0'.repeat(Math.max(0, minDisplayDecimals - 1))}1`;
24
+ return `< ${minValue}`;
25
+ }
26
+ if (!hasNonZeroFraction) {
27
+ const sample = new Intl.NumberFormat(locale).format(1000.1);
28
+ const groupSeparator = sample.replace(/\d/g, '')[0] ?? ',';
29
+ const groupedWhole = useGroupSeparator
30
+ ? (wholePart || '0').replace(/\B(?=(\d{3})+(?!\d))/g, groupSeparator)
31
+ : wholePart;
32
+ return groupedWhole;
33
+ }
34
+ const maxDecimals = decimalsToShow ?? fractionPart.length;
35
+ let trimmedFraction = fractionPart.slice(0, Math.max(0, maxDecimals));
36
+ const minDecimals = minimumFractionDigits ?? 0;
37
+ if (trimmedFraction.length < minDecimals) {
38
+ trimmedFraction = trimmedFraction.padEnd(minDecimals, '0');
39
+ }
40
+ const sample = new Intl.NumberFormat(locale).format(1000.1);
41
+ const groupSeparator = sample.replace(/\d/g, '')[0] ?? ',';
42
+ const decimalSeparator = sample.replace(/\d/g, '').slice(-1) ?? '.';
43
+ const groupedWhole = useGroupSeparator
44
+ ? (wholePart || '0').replace(/\B(?=(\d{3})+(?!\d))/g, groupSeparator)
45
+ : wholePart;
46
+ if (!trimmedFraction)
47
+ return groupedWhole;
48
+ return `${groupedWhole}${decimalSeparator}${trimmedFraction}`;
49
+ };
50
+ export const parsedAmount = (amount, decimals) => parseUnits(amount, decimals);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octaflowlabs/onchain-sdk",
3
- "version": "1.0.0-test7",
3
+ "version": "1.1.0",
4
4
  "description": "onchain methods for web3",
5
5
  "repository": "https://github.com/crisramb665/onchain-sdk.git",
6
6
  "license": "MIT",