@magnaflow/chain-provider 1.0.1
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 +64 -0
- package/dist/chain-factory.d.ts +24 -0
- package/dist/chain-factory.d.ts.map +1 -0
- package/dist/chain-factory.js +39 -0
- package/dist/chain-provider.d.ts +46 -0
- package/dist/chain-provider.d.ts.map +1 -0
- package/dist/chain-provider.js +5 -0
- package/dist/evm-chain-utils.d.ts +22 -0
- package/dist/evm-chain-utils.d.ts.map +1 -0
- package/dist/evm-chain-utils.js +51 -0
- package/dist/evm-provider.d.ts +207 -0
- package/dist/evm-provider.d.ts.map +1 -0
- package/dist/evm-provider.js +218 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/tron-provider.d.ts +31 -0
- package/dist/tron-provider.d.ts.map +1 -0
- package/dist/tron-provider.js +130 -0
- package/dist/types.d.ts +70 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @magnaflow/chain-provider
|
|
2
|
+
|
|
3
|
+
Chain provider abstractions for unified EVM and Tron operations.
|
|
4
|
+
|
|
5
|
+
## What It Provides
|
|
6
|
+
|
|
7
|
+
- A shared `ChainProvider` interface for balances, transfers, receipts, and gas estimation
|
|
8
|
+
- `EvmProvider` backed by `viem`
|
|
9
|
+
- `TronProvider` backed by `tronweb`
|
|
10
|
+
- Factory helpers for creating providers from backend chain configuration
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @magnaflow/chain-provider
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Key Exports
|
|
19
|
+
|
|
20
|
+
- `createChainProvider`
|
|
21
|
+
- `createEvmProvider`
|
|
22
|
+
- `createTronProvider`
|
|
23
|
+
- `getChainType`
|
|
24
|
+
- `EvmProvider`
|
|
25
|
+
- `TronProvider`
|
|
26
|
+
- `createViemChainFromConfig`
|
|
27
|
+
|
|
28
|
+
## Example
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { createChainProvider } from '@magnaflow/chain-provider';
|
|
32
|
+
|
|
33
|
+
const provider = createChainProvider(
|
|
34
|
+
{
|
|
35
|
+
name: 'BSC Mainnet',
|
|
36
|
+
chain: 'bsc',
|
|
37
|
+
rpc_url: 'https://bsc-dataseed.binance.org',
|
|
38
|
+
rest_url: 'https://bsc-dataseed.binance.org',
|
|
39
|
+
payout_contract: '0x0000000000000000000000000000000000000000',
|
|
40
|
+
},
|
|
41
|
+
process.env.MNEMONIC,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const balances = await provider.getBalances('0x1234...', [
|
|
45
|
+
'0x55d398326f99059fF775485246999027B3197955',
|
|
46
|
+
]);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Notes
|
|
50
|
+
|
|
51
|
+
- The default EVM chain resolver is currently tuned for BSC and BSC testnet style configs.
|
|
52
|
+
- `TronProvider` supports native transfers and balance queries. Advanced Tron contract interaction lives outside this package.
|
|
53
|
+
|
|
54
|
+
## Development
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
bunx nx run chain-provider:lint
|
|
58
|
+
bunx nx run chain-provider:test
|
|
59
|
+
bunx nx run chain-provider:build
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
MIT
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chain factory for creating chain providers
|
|
3
|
+
*/
|
|
4
|
+
import type { ChainConfig, ChainType } from './types';
|
|
5
|
+
import type { ChainProvider } from './chain-provider';
|
|
6
|
+
import { EvmProvider } from './evm-provider';
|
|
7
|
+
import { TronProvider } from './tron-provider';
|
|
8
|
+
/**
|
|
9
|
+
* Determine chain type from chain identifier
|
|
10
|
+
*/
|
|
11
|
+
export declare function getChainType(chainIdentifier: string): ChainType;
|
|
12
|
+
/**
|
|
13
|
+
* Create chain provider from configuration
|
|
14
|
+
*/
|
|
15
|
+
export declare function createChainProvider(chainConfig: ChainConfig, mnemonic?: string, accountIndex?: number): ChainProvider;
|
|
16
|
+
/**
|
|
17
|
+
* Create EVM provider
|
|
18
|
+
*/
|
|
19
|
+
export declare function createEvmProvider(chainConfig: ChainConfig, mnemonic?: string, accountIndex?: number): EvmProvider;
|
|
20
|
+
/**
|
|
21
|
+
* Create Tron provider
|
|
22
|
+
*/
|
|
23
|
+
export declare function createTronProvider(chainConfig: ChainConfig, mnemonic?: string, accountIndex?: number): TronProvider;
|
|
24
|
+
//# sourceMappingURL=chain-factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chain-factory.d.ts","sourceRoot":"","sources":["../src/chain-factory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C;;GAEG;AACH,wBAAgB,YAAY,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,CAM/D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,EACxB,QAAQ,CAAC,EAAE,MAAM,EACjB,YAAY,GAAE,MAAU,GACvB,aAAa,CAQf;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,WAAW,EACxB,QAAQ,CAAC,EAAE,MAAM,EACjB,YAAY,GAAE,MAAU,GACvB,WAAW,CAEb;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,WAAW,EACxB,QAAQ,CAAC,EAAE,MAAM,EACjB,YAAY,GAAE,MAAU,GACvB,YAAY,CAEd"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chain factory for creating chain providers
|
|
3
|
+
*/
|
|
4
|
+
import { EvmProvider } from './evm-provider';
|
|
5
|
+
import { TronProvider } from './tron-provider';
|
|
6
|
+
/**
|
|
7
|
+
* Determine chain type from chain identifier
|
|
8
|
+
*/
|
|
9
|
+
export function getChainType(chainIdentifier) {
|
|
10
|
+
const lower = chainIdentifier.toLowerCase();
|
|
11
|
+
if (lower.includes('trx') || lower.includes('tron')) {
|
|
12
|
+
return 'tron';
|
|
13
|
+
}
|
|
14
|
+
return 'evm';
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create chain provider from configuration
|
|
18
|
+
*/
|
|
19
|
+
export function createChainProvider(chainConfig, mnemonic, accountIndex = 0) {
|
|
20
|
+
const chainType = getChainType(chainConfig.chain);
|
|
21
|
+
if (chainType === 'evm') {
|
|
22
|
+
return new EvmProvider(chainConfig, mnemonic, accountIndex);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
return new TronProvider(chainConfig, mnemonic, accountIndex);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Create EVM provider
|
|
30
|
+
*/
|
|
31
|
+
export function createEvmProvider(chainConfig, mnemonic, accountIndex = 0) {
|
|
32
|
+
return new EvmProvider(chainConfig, mnemonic, accountIndex);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create Tron provider
|
|
36
|
+
*/
|
|
37
|
+
export function createTronProvider(chainConfig, mnemonic, accountIndex = 0) {
|
|
38
|
+
return new TronProvider(chainConfig, mnemonic, accountIndex);
|
|
39
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chain provider interface
|
|
3
|
+
* Defines the contract for chain-specific implementations
|
|
4
|
+
*/
|
|
5
|
+
import type { ChainType, BalanceInfo, TransactionOptions, TransactionResult, TransactionReceipt } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Chain provider interface
|
|
8
|
+
*/
|
|
9
|
+
export interface ChainProvider {
|
|
10
|
+
/**
|
|
11
|
+
* Get chain type
|
|
12
|
+
*/
|
|
13
|
+
getChainType(): ChainType;
|
|
14
|
+
/**
|
|
15
|
+
* Get native balance for an address
|
|
16
|
+
*/
|
|
17
|
+
getNativeBalance(address: string): Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Get token balance for an address
|
|
20
|
+
*/
|
|
21
|
+
getTokenBalance(tokenAddress: string, address: string, decimals?: number): Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Get multiple balances (native + tokens)
|
|
24
|
+
*/
|
|
25
|
+
getBalances(address: string, tokenAddresses?: string[]): Promise<BalanceInfo>;
|
|
26
|
+
/**
|
|
27
|
+
* Send native token transaction
|
|
28
|
+
*/
|
|
29
|
+
sendTransaction(from: string, to: string, amount: string, options?: TransactionOptions): Promise<TransactionResult>;
|
|
30
|
+
/**
|
|
31
|
+
* Wait for transaction confirmation
|
|
32
|
+
*/
|
|
33
|
+
waitForTransaction(txHash: string, options?: {
|
|
34
|
+
timeout?: number;
|
|
35
|
+
confirmations?: number;
|
|
36
|
+
}): Promise<TransactionReceipt>;
|
|
37
|
+
/**
|
|
38
|
+
* Get transaction receipt
|
|
39
|
+
*/
|
|
40
|
+
getTransactionReceipt(txHash: string): Promise<TransactionReceipt | null>;
|
|
41
|
+
/**
|
|
42
|
+
* Estimate gas for a transaction
|
|
43
|
+
*/
|
|
44
|
+
estimateGas(from: string, to: string, data?: string, value?: bigint): Promise<bigint>;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=chain-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chain-provider.d.ts","sourceRoot":"","sources":["../src/chain-provider.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,YAAY,IAAI,SAAS,CAAC;IAE1B;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnD;;OAEG;IACH,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE3F;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAE9E;;OAEG;IACH,eAAe,CACb,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAE9B;;OAEG;IACH,kBAAkB,CAChB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GACrD,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAE/B;;OAEG;IACH,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IAE1E;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACvF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Chain } from 'viem';
|
|
2
|
+
import type { ChainConfig } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Interface layer: strategy type for resolving a runtime EVM chain
|
|
5
|
+
* configuration from backend-provided ChainConfig.
|
|
6
|
+
*/
|
|
7
|
+
export type EvmChainRuntimeResolver = (config: ChainConfig) => Chain;
|
|
8
|
+
/**
|
|
9
|
+
* Implementation layer: default resolver that normalizes ChainConfig
|
|
10
|
+
* into a viem Chain definition.
|
|
11
|
+
*
|
|
12
|
+
* Rules:
|
|
13
|
+
* - BSC testnet detection is based on rest_url and chain identifier.
|
|
14
|
+
* - Default mainnet id is 56 (BSC mainnet) when not on testnet.
|
|
15
|
+
* - scan_url, if present, is used as the default block explorer URL.
|
|
16
|
+
*/
|
|
17
|
+
export declare const defaultEvmChainRuntimeResolver: EvmChainRuntimeResolver;
|
|
18
|
+
/**
|
|
19
|
+
* Convenience export used by application code.
|
|
20
|
+
*/
|
|
21
|
+
export declare const createViemChainFromConfig: EvmChainRuntimeResolver;
|
|
22
|
+
//# sourceMappingURL=evm-chain-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evm-chain-utils.d.ts","sourceRoot":"","sources":["../src/evm-chain-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG3C;;;GAGG;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,MAAM,EAAE,WAAW,KAAK,KAAK,CAAC;AAErE;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,EAAE,uBAyC5C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,yBAAyB,EAAE,uBAAwD,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { bscTestnet, bsc } from 'viem/chains';
|
|
2
|
+
/**
|
|
3
|
+
* Implementation layer: default resolver that normalizes ChainConfig
|
|
4
|
+
* into a viem Chain definition.
|
|
5
|
+
*
|
|
6
|
+
* Rules:
|
|
7
|
+
* - BSC testnet detection is based on rest_url and chain identifier.
|
|
8
|
+
* - Default mainnet id is 56 (BSC mainnet) when not on testnet.
|
|
9
|
+
* - scan_url, if present, is used as the default block explorer URL.
|
|
10
|
+
*/
|
|
11
|
+
export const defaultEvmChainRuntimeResolver = (chainConfig) => {
|
|
12
|
+
const isBscTestnet = chainConfig.rest_url.includes('prebsc') ||
|
|
13
|
+
chainConfig.rest_url.includes('bsc-testnet') ||
|
|
14
|
+
chainConfig.chain?.toLowerCase() === 'bsc_test';
|
|
15
|
+
if (isBscTestnet) {
|
|
16
|
+
return {
|
|
17
|
+
...bscTestnet,
|
|
18
|
+
rpcUrls: {
|
|
19
|
+
default: {
|
|
20
|
+
http: [chainConfig.rest_url],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
blockExplorers: {
|
|
24
|
+
default: {
|
|
25
|
+
name: 'BscScan',
|
|
26
|
+
url: chainConfig.scan_url || bscTestnet.blockExplorers?.default?.url,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
...bsc,
|
|
33
|
+
rpcUrls: {
|
|
34
|
+
default: {
|
|
35
|
+
http: [chainConfig.rest_url],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
blockExplorers: chainConfig.scan_url
|
|
39
|
+
? {
|
|
40
|
+
default: {
|
|
41
|
+
name: 'Explorer',
|
|
42
|
+
url: chainConfig.scan_url,
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
: undefined,
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Convenience export used by application code.
|
|
50
|
+
*/
|
|
51
|
+
export const createViemChainFromConfig = defaultEvmChainRuntimeResolver;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EVM chain provider implementation
|
|
3
|
+
*/
|
|
4
|
+
import { type PublicClient, type WalletClient } from 'viem';
|
|
5
|
+
import type { ChainConfig, BalanceInfo, TransactionOptions, TransactionResult, TransactionReceipt } from './types';
|
|
6
|
+
import type { ChainProvider } from './chain-provider';
|
|
7
|
+
/**
|
|
8
|
+
* EVM chain provider
|
|
9
|
+
*/
|
|
10
|
+
export declare class EvmProvider implements ChainProvider {
|
|
11
|
+
private chainConfig;
|
|
12
|
+
private publicClient;
|
|
13
|
+
private chain;
|
|
14
|
+
private account?;
|
|
15
|
+
constructor(chainConfig: ChainConfig, mnemonic?: string, accountIndex?: number);
|
|
16
|
+
getChainType(): 'evm';
|
|
17
|
+
getNativeBalance(address: string): Promise<string>;
|
|
18
|
+
getTokenBalance(tokenAddress: string, address: string, decimals?: number): Promise<string>;
|
|
19
|
+
getBalances(address: string, tokenAddresses?: string[]): Promise<BalanceInfo>;
|
|
20
|
+
sendTransaction(_from: string, to: string, amount: string, options?: TransactionOptions): Promise<TransactionResult>;
|
|
21
|
+
waitForTransaction(txHash: string, options?: {
|
|
22
|
+
timeout?: number;
|
|
23
|
+
confirmations?: number;
|
|
24
|
+
}): Promise<TransactionReceipt>;
|
|
25
|
+
getTransactionReceipt(txHash: string): Promise<TransactionReceipt | null>;
|
|
26
|
+
estimateGas(from: string, to: string, data?: string, value?: bigint): Promise<bigint>;
|
|
27
|
+
/**
|
|
28
|
+
* Get block number (using viem)
|
|
29
|
+
*/
|
|
30
|
+
getBlockNumber(): Promise<bigint>;
|
|
31
|
+
/**
|
|
32
|
+
* Get block (using viem)
|
|
33
|
+
*/
|
|
34
|
+
getBlock(blockNumber?: bigint): Promise<{
|
|
35
|
+
number: bigint;
|
|
36
|
+
nonce: `0x${string}`;
|
|
37
|
+
hash: `0x${string}`;
|
|
38
|
+
gasUsed: bigint;
|
|
39
|
+
logsBloom: `0x${string}`;
|
|
40
|
+
baseFeePerGas: bigint | null;
|
|
41
|
+
blobGasUsed: bigint;
|
|
42
|
+
difficulty: bigint;
|
|
43
|
+
excessBlobGas: bigint;
|
|
44
|
+
extraData: import("viem").Hex;
|
|
45
|
+
gasLimit: bigint;
|
|
46
|
+
miner: import("viem").Address;
|
|
47
|
+
mixHash: import("viem").Hash;
|
|
48
|
+
parentBeaconBlockRoot?: `0x${string}` | undefined;
|
|
49
|
+
parentHash: import("viem").Hash;
|
|
50
|
+
receiptsRoot: import("viem").Hex;
|
|
51
|
+
sealFields: import("viem").Hex[];
|
|
52
|
+
sha3Uncles: import("viem").Hash;
|
|
53
|
+
size: bigint;
|
|
54
|
+
stateRoot: import("viem").Hash;
|
|
55
|
+
timestamp: bigint;
|
|
56
|
+
totalDifficulty: bigint | null;
|
|
57
|
+
transactionsRoot: import("viem").Hash;
|
|
58
|
+
uncles: import("viem").Hash[];
|
|
59
|
+
withdrawals?: import("viem").Withdrawal[] | undefined | undefined;
|
|
60
|
+
withdrawalsRoot?: `0x${string}` | undefined;
|
|
61
|
+
transactions: `0x${string}`[];
|
|
62
|
+
}>;
|
|
63
|
+
/**
|
|
64
|
+
* Get transaction (using viem)
|
|
65
|
+
*/
|
|
66
|
+
getTransaction(txHash: string): Promise<{
|
|
67
|
+
from: import("viem").Address;
|
|
68
|
+
gas: bigint;
|
|
69
|
+
nonce: number;
|
|
70
|
+
to: import("viem").Address | null;
|
|
71
|
+
type: "legacy";
|
|
72
|
+
value: bigint;
|
|
73
|
+
r: import("viem").Hex;
|
|
74
|
+
s: import("viem").Hex;
|
|
75
|
+
v: bigint;
|
|
76
|
+
yParity?: undefined | undefined;
|
|
77
|
+
blobVersionedHashes?: undefined | undefined;
|
|
78
|
+
gasPrice: bigint;
|
|
79
|
+
maxFeePerBlobGas?: undefined | undefined;
|
|
80
|
+
maxFeePerGas?: undefined | undefined;
|
|
81
|
+
maxPriorityFeePerGas?: undefined | undefined;
|
|
82
|
+
chainId?: number | undefined;
|
|
83
|
+
accessList?: undefined | undefined;
|
|
84
|
+
authorizationList?: undefined | undefined;
|
|
85
|
+
hash: import("viem").Hash;
|
|
86
|
+
input: import("viem").Hex;
|
|
87
|
+
typeHex: import("viem").Hex | null;
|
|
88
|
+
blockNumber: bigint;
|
|
89
|
+
blockHash: `0x${string}`;
|
|
90
|
+
transactionIndex: number;
|
|
91
|
+
} | {
|
|
92
|
+
from: import("viem").Address;
|
|
93
|
+
gas: bigint;
|
|
94
|
+
nonce: number;
|
|
95
|
+
to: import("viem").Address | null;
|
|
96
|
+
type: "eip2930";
|
|
97
|
+
value: bigint;
|
|
98
|
+
r: import("viem").Hex;
|
|
99
|
+
s: import("viem").Hex;
|
|
100
|
+
v: bigint;
|
|
101
|
+
yParity: number;
|
|
102
|
+
blobVersionedHashes?: undefined | undefined;
|
|
103
|
+
gasPrice: bigint;
|
|
104
|
+
maxFeePerBlobGas?: undefined | undefined;
|
|
105
|
+
maxFeePerGas?: undefined | undefined;
|
|
106
|
+
maxPriorityFeePerGas?: undefined | undefined;
|
|
107
|
+
chainId: number;
|
|
108
|
+
accessList: import("viem").AccessList;
|
|
109
|
+
authorizationList?: undefined | undefined;
|
|
110
|
+
hash: import("viem").Hash;
|
|
111
|
+
input: import("viem").Hex;
|
|
112
|
+
typeHex: import("viem").Hex | null;
|
|
113
|
+
blockNumber: bigint;
|
|
114
|
+
blockHash: `0x${string}`;
|
|
115
|
+
transactionIndex: number;
|
|
116
|
+
} | {
|
|
117
|
+
from: import("viem").Address;
|
|
118
|
+
gas: bigint;
|
|
119
|
+
nonce: number;
|
|
120
|
+
to: import("viem").Address | null;
|
|
121
|
+
type: "eip1559";
|
|
122
|
+
value: bigint;
|
|
123
|
+
r: import("viem").Hex;
|
|
124
|
+
s: import("viem").Hex;
|
|
125
|
+
v: bigint;
|
|
126
|
+
yParity: number;
|
|
127
|
+
blobVersionedHashes?: undefined | undefined;
|
|
128
|
+
gasPrice?: undefined | undefined;
|
|
129
|
+
maxFeePerBlobGas?: undefined | undefined;
|
|
130
|
+
maxFeePerGas: bigint;
|
|
131
|
+
maxPriorityFeePerGas: bigint;
|
|
132
|
+
chainId: number;
|
|
133
|
+
accessList: import("viem").AccessList;
|
|
134
|
+
authorizationList?: undefined | undefined;
|
|
135
|
+
hash: import("viem").Hash;
|
|
136
|
+
input: import("viem").Hex;
|
|
137
|
+
typeHex: import("viem").Hex | null;
|
|
138
|
+
blockNumber: bigint;
|
|
139
|
+
blockHash: `0x${string}`;
|
|
140
|
+
transactionIndex: number;
|
|
141
|
+
} | {
|
|
142
|
+
from: import("viem").Address;
|
|
143
|
+
gas: bigint;
|
|
144
|
+
nonce: number;
|
|
145
|
+
to: import("viem").Address | null;
|
|
146
|
+
type: "eip4844";
|
|
147
|
+
value: bigint;
|
|
148
|
+
r: import("viem").Hex;
|
|
149
|
+
s: import("viem").Hex;
|
|
150
|
+
v: bigint;
|
|
151
|
+
yParity: number;
|
|
152
|
+
blobVersionedHashes: readonly import("viem").Hex[];
|
|
153
|
+
gasPrice?: undefined | undefined;
|
|
154
|
+
maxFeePerBlobGas: bigint;
|
|
155
|
+
maxFeePerGas: bigint;
|
|
156
|
+
maxPriorityFeePerGas: bigint;
|
|
157
|
+
chainId: number;
|
|
158
|
+
accessList: import("viem").AccessList;
|
|
159
|
+
authorizationList?: undefined | undefined;
|
|
160
|
+
hash: import("viem").Hash;
|
|
161
|
+
input: import("viem").Hex;
|
|
162
|
+
typeHex: import("viem").Hex | null;
|
|
163
|
+
blockNumber: bigint;
|
|
164
|
+
blockHash: `0x${string}`;
|
|
165
|
+
transactionIndex: number;
|
|
166
|
+
} | {
|
|
167
|
+
from: import("viem").Address;
|
|
168
|
+
gas: bigint;
|
|
169
|
+
nonce: number;
|
|
170
|
+
to: import("viem").Address | null;
|
|
171
|
+
type: "eip7702";
|
|
172
|
+
value: bigint;
|
|
173
|
+
r: import("viem").Hex;
|
|
174
|
+
s: import("viem").Hex;
|
|
175
|
+
v: bigint;
|
|
176
|
+
yParity: number;
|
|
177
|
+
blobVersionedHashes?: undefined | undefined;
|
|
178
|
+
gasPrice?: undefined | undefined;
|
|
179
|
+
maxFeePerBlobGas?: undefined | undefined;
|
|
180
|
+
maxFeePerGas: bigint;
|
|
181
|
+
maxPriorityFeePerGas: bigint;
|
|
182
|
+
chainId: number;
|
|
183
|
+
accessList: import("viem").AccessList;
|
|
184
|
+
authorizationList: import("viem").SignedAuthorizationList;
|
|
185
|
+
hash: import("viem").Hash;
|
|
186
|
+
input: import("viem").Hex;
|
|
187
|
+
typeHex: import("viem").Hex | null;
|
|
188
|
+
blockNumber: bigint;
|
|
189
|
+
blockHash: `0x${string}`;
|
|
190
|
+
transactionIndex: number;
|
|
191
|
+
}>;
|
|
192
|
+
/**
|
|
193
|
+
* Get fee data (using viem)
|
|
194
|
+
*/
|
|
195
|
+
getFeeData(): Promise<import("viem").FeeValuesEIP1559>;
|
|
196
|
+
/**
|
|
197
|
+
* Get public client (for advanced usage)
|
|
198
|
+
*/
|
|
199
|
+
getPublicClient(): PublicClient;
|
|
200
|
+
/**
|
|
201
|
+
* Get wallet client (for advanced usage)
|
|
202
|
+
* Uses lazy initialization to avoid creating multiple instances
|
|
203
|
+
*/
|
|
204
|
+
private _walletClient;
|
|
205
|
+
getWalletClient(): WalletClient | null;
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=evm-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evm-provider.d.ts","sourceRoot":"","sources":["../src/evm-provider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAIL,KAAK,YAAY,EAEjB,KAAK,YAAY,EAElB,MAAM,MAAM,CAAC;AAEd,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAItD;;GAEG;AACH,qBAAa,WAAY,YAAW,aAAa;IAC/C,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,OAAO,CAAC,CAAU;gBAEd,WAAW,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,YAAY,GAAE,MAAU;IAgBjF,YAAY,IAAI,KAAK;IAIf,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOlD,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuB1F,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,WAAW,CAAC;IAuCjF,eAAe,CACnB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,iBAAiB,CAAC;IAmCvB,kBAAkB,CACtB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GACrD,OAAO,CAAC,kBAAkB,CAAC;IAmBxB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAoBzE,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAU3F;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAIvC;;OAEG;IACG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAOnC;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAMnC;;OAEG;IACG,UAAU;IAIhB;;OAEG;IACH,eAAe,IAAI,YAAY;IAI/B;;;OAGG;IACH,OAAO,CAAC,aAAa,CAA6B;IAClD,eAAe,IAAI,YAAY,GAAG,IAAI;CAavC"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EVM chain provider implementation
|
|
3
|
+
*/
|
|
4
|
+
import { createPublicClient, createWalletClient, http, } from 'viem';
|
|
5
|
+
import { mnemonicToAccount } from 'viem/accounts';
|
|
6
|
+
import { DERIVATION_PATH_BASE, buildDerivationPath } from '@magnaflow/eoa-wallet';
|
|
7
|
+
import { createViemChainFromConfig } from './evm-chain-utils';
|
|
8
|
+
/**
|
|
9
|
+
* EVM chain provider
|
|
10
|
+
*/
|
|
11
|
+
export class EvmProvider {
|
|
12
|
+
constructor(chainConfig, mnemonic, accountIndex = 0) {
|
|
13
|
+
/**
|
|
14
|
+
* Get wallet client (for advanced usage)
|
|
15
|
+
* Uses lazy initialization to avoid creating multiple instances
|
|
16
|
+
*/
|
|
17
|
+
this._walletClient = null;
|
|
18
|
+
this.chainConfig = chainConfig;
|
|
19
|
+
this.chain = createViemChainFromConfig(chainConfig);
|
|
20
|
+
this.publicClient = createPublicClient({
|
|
21
|
+
chain: this.chain,
|
|
22
|
+
transport: http(chainConfig.rest_url),
|
|
23
|
+
});
|
|
24
|
+
if (mnemonic) {
|
|
25
|
+
const path = buildDerivationPath(DERIVATION_PATH_BASE.evm, accountIndex);
|
|
26
|
+
this.account = mnemonicToAccount(mnemonic, {
|
|
27
|
+
path: path,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
getChainType() {
|
|
32
|
+
return 'evm';
|
|
33
|
+
}
|
|
34
|
+
async getNativeBalance(address) {
|
|
35
|
+
const balance = await this.publicClient.getBalance({
|
|
36
|
+
address: address,
|
|
37
|
+
});
|
|
38
|
+
return balance.toString();
|
|
39
|
+
}
|
|
40
|
+
async getTokenBalance(tokenAddress, address, decimals) {
|
|
41
|
+
// decimals parameter is kept for API consistency but not used
|
|
42
|
+
// Token decimals are handled by the caller if needed
|
|
43
|
+
void decimals;
|
|
44
|
+
// Use viem's readContract with standard ERC20 ABI
|
|
45
|
+
const balance = await this.publicClient.readContract({
|
|
46
|
+
address: tokenAddress,
|
|
47
|
+
abi: [
|
|
48
|
+
{
|
|
49
|
+
type: 'function',
|
|
50
|
+
name: 'balanceOf',
|
|
51
|
+
inputs: [{ name: 'account', type: 'address' }],
|
|
52
|
+
outputs: [{ name: '', type: 'uint256' }],
|
|
53
|
+
stateMutability: 'view',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
functionName: 'balanceOf',
|
|
57
|
+
args: [address],
|
|
58
|
+
});
|
|
59
|
+
return balance.toString();
|
|
60
|
+
}
|
|
61
|
+
async getBalances(address, tokenAddresses = []) {
|
|
62
|
+
// Use viem's multicall for batch reads (more efficient)
|
|
63
|
+
const erc20Abi = [
|
|
64
|
+
{
|
|
65
|
+
type: 'function',
|
|
66
|
+
name: 'balanceOf',
|
|
67
|
+
inputs: [{ name: 'account', type: 'address' }],
|
|
68
|
+
outputs: [{ name: '', type: 'uint256' }],
|
|
69
|
+
stateMutability: 'view',
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
const calls = tokenAddresses.map((tokenAddress) => ({
|
|
73
|
+
address: tokenAddress,
|
|
74
|
+
abi: erc20Abi,
|
|
75
|
+
functionName: 'balanceOf',
|
|
76
|
+
args: [address],
|
|
77
|
+
}));
|
|
78
|
+
// Get native balance and token balances in parallel
|
|
79
|
+
const [native, tokenBalances] = await Promise.all([
|
|
80
|
+
this.getNativeBalance(address),
|
|
81
|
+
calls.length > 0 ? this.publicClient.multicall({ contracts: calls }) : Promise.resolve([]),
|
|
82
|
+
]);
|
|
83
|
+
// Build tokens record
|
|
84
|
+
const tokens = {};
|
|
85
|
+
tokenAddresses.forEach((tokenAddress, index) => {
|
|
86
|
+
const result = tokenBalances[index];
|
|
87
|
+
if (result && 'status' in result && result.status === 'success') {
|
|
88
|
+
tokens[tokenAddress] = result.result.toString();
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
tokens[tokenAddress] = '0';
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
return { native, tokens };
|
|
95
|
+
}
|
|
96
|
+
async sendTransaction(_from, to, amount, options) {
|
|
97
|
+
if (!this.account) {
|
|
98
|
+
throw new Error('Account not initialized. Provide mnemonic in constructor.');
|
|
99
|
+
}
|
|
100
|
+
// Use cached wallet client or create new one
|
|
101
|
+
const walletClient = this.getWalletClient();
|
|
102
|
+
if (!walletClient) {
|
|
103
|
+
throw new Error('Wallet client not available');
|
|
104
|
+
}
|
|
105
|
+
// Use viem's sendTransaction with all options
|
|
106
|
+
// Note: gasPrice and maxFeePerGas/maxPriorityFeePerGas are mutually exclusive
|
|
107
|
+
const txParams = {
|
|
108
|
+
to: to,
|
|
109
|
+
value: BigInt(amount),
|
|
110
|
+
gas: options?.gasLimit,
|
|
111
|
+
};
|
|
112
|
+
// Use EIP-1559 fees if provided, otherwise fall back to gasPrice
|
|
113
|
+
if (options?.maxFeePerGas || options?.maxPriorityFeePerGas) {
|
|
114
|
+
txParams.maxFeePerGas = options?.maxFeePerGas;
|
|
115
|
+
txParams.maxPriorityFeePerGas = options?.maxPriorityFeePerGas;
|
|
116
|
+
}
|
|
117
|
+
else if (options?.gasPrice) {
|
|
118
|
+
txParams.gasPrice = options.gasPrice;
|
|
119
|
+
}
|
|
120
|
+
const hash = await walletClient.sendTransaction(txParams);
|
|
121
|
+
return {
|
|
122
|
+
hash,
|
|
123
|
+
chainType: 'evm',
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
async waitForTransaction(txHash, options) {
|
|
127
|
+
// Use viem's waitForTransactionReceipt directly
|
|
128
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({
|
|
129
|
+
hash: txHash,
|
|
130
|
+
timeout: options?.timeout,
|
|
131
|
+
confirmations: options?.confirmations,
|
|
132
|
+
});
|
|
133
|
+
// Map viem receipt to our receipt format
|
|
134
|
+
return {
|
|
135
|
+
hash: receipt.transactionHash,
|
|
136
|
+
status: receipt.status === 'success' ? 'success' : 'failed',
|
|
137
|
+
blockNumber: Number(receipt.blockNumber),
|
|
138
|
+
blockHash: receipt.blockHash,
|
|
139
|
+
gasUsed: receipt.gasUsed,
|
|
140
|
+
effectiveGasPrice: receipt.effectiveGasPrice,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
async getTransactionReceipt(txHash) {
|
|
144
|
+
// Use viem's getTransactionReceipt directly
|
|
145
|
+
try {
|
|
146
|
+
const receipt = await this.publicClient.getTransactionReceipt({
|
|
147
|
+
hash: txHash,
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
hash: receipt.transactionHash,
|
|
151
|
+
status: receipt.status === 'success' ? 'success' : 'failed',
|
|
152
|
+
blockNumber: Number(receipt.blockNumber),
|
|
153
|
+
blockHash: receipt.blockHash,
|
|
154
|
+
gasUsed: receipt.gasUsed,
|
|
155
|
+
effectiveGasPrice: receipt.effectiveGasPrice,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async estimateGas(from, to, data, value) {
|
|
163
|
+
// Use viem's estimateGas directly
|
|
164
|
+
return await this.publicClient.estimateGas({
|
|
165
|
+
account: from,
|
|
166
|
+
to: to,
|
|
167
|
+
data: data,
|
|
168
|
+
value,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get block number (using viem)
|
|
173
|
+
*/
|
|
174
|
+
async getBlockNumber() {
|
|
175
|
+
return await this.publicClient.getBlockNumber();
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Get block (using viem)
|
|
179
|
+
*/
|
|
180
|
+
async getBlock(blockNumber) {
|
|
181
|
+
if (blockNumber) {
|
|
182
|
+
return await this.publicClient.getBlock({ blockNumber });
|
|
183
|
+
}
|
|
184
|
+
return await this.publicClient.getBlock();
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get transaction (using viem)
|
|
188
|
+
*/
|
|
189
|
+
async getTransaction(txHash) {
|
|
190
|
+
return await this.publicClient.getTransaction({
|
|
191
|
+
hash: txHash,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Get fee data (using viem)
|
|
196
|
+
*/
|
|
197
|
+
async getFeeData() {
|
|
198
|
+
return await this.publicClient.estimateFeesPerGas();
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Get public client (for advanced usage)
|
|
202
|
+
*/
|
|
203
|
+
getPublicClient() {
|
|
204
|
+
return this.publicClient;
|
|
205
|
+
}
|
|
206
|
+
getWalletClient() {
|
|
207
|
+
if (!this.account)
|
|
208
|
+
return null;
|
|
209
|
+
if (!this._walletClient) {
|
|
210
|
+
this._walletClient = createWalletClient({
|
|
211
|
+
account: this.account,
|
|
212
|
+
chain: this.chain,
|
|
213
|
+
transport: http(this.chainConfig.rest_url),
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return this._walletClient;
|
|
217
|
+
}
|
|
218
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @magnaflow/chain-provider
|
|
3
|
+
* Chain provider abstraction library for unified blockchain interactions
|
|
4
|
+
*/
|
|
5
|
+
export type { ChainType, ChainConfig, TokenConfig, BalanceInfo, TransactionOptions, TransactionResult, TransactionReceipt, } from './types';
|
|
6
|
+
export type { ChainProvider } from './chain-provider';
|
|
7
|
+
export { EvmProvider } from './evm-provider';
|
|
8
|
+
export { TronProvider } from './tron-provider';
|
|
9
|
+
export { createViemChainFromConfig } from './evm-chain-utils';
|
|
10
|
+
export { getChainType, createChainProvider, createEvmProvider, createTronProvider, } from './chain-factory';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,YAAY,EACV,SAAS,EACT,WAAW,EACX,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,SAAS,CAAC;AAGjB,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAG9D,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @magnaflow/chain-provider
|
|
3
|
+
* Chain provider abstraction library for unified blockchain interactions
|
|
4
|
+
*/
|
|
5
|
+
// Providers
|
|
6
|
+
export { EvmProvider } from './evm-provider';
|
|
7
|
+
export { TronProvider } from './tron-provider';
|
|
8
|
+
export { createViemChainFromConfig } from './evm-chain-utils';
|
|
9
|
+
// Factory
|
|
10
|
+
export { getChainType, createChainProvider, createEvmProvider, createTronProvider, } from './chain-factory';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tron chain provider implementation
|
|
3
|
+
*/
|
|
4
|
+
import { TronWeb } from 'tronweb';
|
|
5
|
+
import type { ChainConfig, BalanceInfo, TransactionOptions, TransactionResult, TransactionReceipt } from './types';
|
|
6
|
+
import type { ChainProvider } from './chain-provider';
|
|
7
|
+
/**
|
|
8
|
+
* Tron chain provider
|
|
9
|
+
*/
|
|
10
|
+
export declare class TronProvider implements ChainProvider {
|
|
11
|
+
private chainConfig;
|
|
12
|
+
private tronWeb;
|
|
13
|
+
private privateKey?;
|
|
14
|
+
constructor(chainConfig: ChainConfig, mnemonic?: string, accountIndex?: number);
|
|
15
|
+
getChainType(): 'tron';
|
|
16
|
+
getNativeBalance(address: string): Promise<string>;
|
|
17
|
+
getTokenBalance(tokenAddress: string, address: string, _decimals?: number): Promise<string>;
|
|
18
|
+
getBalances(address: string, tokenAddresses?: string[]): Promise<BalanceInfo>;
|
|
19
|
+
sendTransaction(_from: string, to: string, amount: string, _options?: TransactionOptions): Promise<TransactionResult>;
|
|
20
|
+
waitForTransaction(txHash: string, options?: {
|
|
21
|
+
timeout?: number;
|
|
22
|
+
confirmations?: number;
|
|
23
|
+
}): Promise<TransactionReceipt>;
|
|
24
|
+
getTransactionReceipt(txHash: string): Promise<TransactionReceipt | null>;
|
|
25
|
+
estimateGas(_from: string, _to: string, _data?: string, _value?: bigint): Promise<bigint>;
|
|
26
|
+
/**
|
|
27
|
+
* Get TronWeb instance (for advanced usage)
|
|
28
|
+
*/
|
|
29
|
+
getTronWeb(): TronWeb;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=tron-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tron-provider.d.ts","sourceRoot":"","sources":["../src/tron-provider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD;;GAEG;AACH,qBAAa,YAAa,YAAW,aAAa;IAChD,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,UAAU,CAAC,CAAS;gBAEhB,WAAW,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,YAAY,GAAE,MAAU;IAsBjF,YAAY,IAAI,MAAM;IAIhB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKlD,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC;IAMZ,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,WAAW,CAAC;IAkBjF,eAAe,CACnB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,kBAAkB,GAC5B,OAAO,CAAC,iBAAiB,CAAC;IAuCvB,kBAAkB,CACtB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GACrD,OAAO,CAAC,kBAAkB,CAAC;IA0BxB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAiBzE,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM/F;;OAEG;IACH,UAAU,IAAI,OAAO;CAGtB"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tron chain provider implementation
|
|
3
|
+
*/
|
|
4
|
+
import { TronWeb } from 'tronweb';
|
|
5
|
+
import { DERIVATION_PATH_BASE, buildDerivationPath } from '@magnaflow/eoa-wallet';
|
|
6
|
+
/**
|
|
7
|
+
* Tron chain provider
|
|
8
|
+
*/
|
|
9
|
+
export class TronProvider {
|
|
10
|
+
constructor(chainConfig, mnemonic, accountIndex = 0) {
|
|
11
|
+
this.chainConfig = chainConfig;
|
|
12
|
+
// Create TronWeb instance
|
|
13
|
+
let fullHost = chainConfig.rest_url;
|
|
14
|
+
if (typeof fullHost === 'string') {
|
|
15
|
+
fullHost = fullHost.replace(/\/jsonrpc\/?$/i, '');
|
|
16
|
+
}
|
|
17
|
+
this.tronWeb = new TronWeb({
|
|
18
|
+
fullHost,
|
|
19
|
+
});
|
|
20
|
+
// Set private key if mnemonic provided
|
|
21
|
+
if (mnemonic) {
|
|
22
|
+
const path = buildDerivationPath(DERIVATION_PATH_BASE.tron, accountIndex);
|
|
23
|
+
const { privateKey } = TronWeb.fromMnemonic(mnemonic, path);
|
|
24
|
+
this.privateKey = privateKey;
|
|
25
|
+
this.tronWeb.setPrivateKey(privateKey);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
getChainType() {
|
|
29
|
+
return 'tron';
|
|
30
|
+
}
|
|
31
|
+
async getNativeBalance(address) {
|
|
32
|
+
const balance = await this.tronWeb.trx.getBalance(address);
|
|
33
|
+
return balance.toString();
|
|
34
|
+
}
|
|
35
|
+
async getTokenBalance(tokenAddress, address, _decimals) {
|
|
36
|
+
const contract = await this.tronWeb.contract().at(tokenAddress);
|
|
37
|
+
const balance = await contract.balanceOf(address).call();
|
|
38
|
+
return balance.toString();
|
|
39
|
+
}
|
|
40
|
+
async getBalances(address, tokenAddresses = []) {
|
|
41
|
+
const native = await this.getNativeBalance(address);
|
|
42
|
+
const tokens = {};
|
|
43
|
+
for (const tokenAddress of tokenAddresses) {
|
|
44
|
+
const tokenConfig = this.chainConfig.tokens?.find((t) => t.contract_addr.toLowerCase() === tokenAddress.toLowerCase());
|
|
45
|
+
tokens[tokenAddress] = await this.getTokenBalance(tokenAddress, address, tokenConfig?.decimal);
|
|
46
|
+
}
|
|
47
|
+
return { native, tokens };
|
|
48
|
+
}
|
|
49
|
+
async sendTransaction(_from, to, amount, _options) {
|
|
50
|
+
if (!this.privateKey) {
|
|
51
|
+
throw new Error('Private key not initialized. Provide mnemonic in constructor.');
|
|
52
|
+
}
|
|
53
|
+
// Amount is expected to be in sun (base units), provided as a decimal string.
|
|
54
|
+
// TronWeb.sendTrx currently accepts a JavaScript number, so we:
|
|
55
|
+
// - parse the value as BigInt to validate it is a non-negative integer
|
|
56
|
+
// - ensure it fits safely in Number without precision loss
|
|
57
|
+
let sunAmount;
|
|
58
|
+
try {
|
|
59
|
+
sunAmount = BigInt(amount);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
throw new Error(`Invalid amount for Tron transaction: "${amount}"`);
|
|
63
|
+
}
|
|
64
|
+
if (sunAmount < 0n) {
|
|
65
|
+
throw new Error('Amount for Tron transaction must be non-negative');
|
|
66
|
+
}
|
|
67
|
+
const maxSafe = BigInt(Number.MAX_SAFE_INTEGER);
|
|
68
|
+
if (sunAmount > maxSafe) {
|
|
69
|
+
throw new Error(`Amount in sun (${sunAmount.toString()}) exceeds JavaScript Number safe range; ` +
|
|
70
|
+
'please split into smaller transfers or use a lower amount.');
|
|
71
|
+
}
|
|
72
|
+
// sendTrx uses the address from setPrivateKey, so we only need to pass target and amount.
|
|
73
|
+
const tx = await this.tronWeb.trx.sendTrx(to, Number(sunAmount));
|
|
74
|
+
// TronWeb sendTrx returns BroadcastReturn which has txid property
|
|
75
|
+
const txHash = typeof tx === 'string' ? tx : tx.txid || tx.hash || String(tx);
|
|
76
|
+
return {
|
|
77
|
+
hash: txHash,
|
|
78
|
+
chainType: 'tron',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async waitForTransaction(txHash, options) {
|
|
82
|
+
const maxRetries = options?.timeout ? Math.floor(options.timeout / 2000) : 30;
|
|
83
|
+
let retryCount = 0;
|
|
84
|
+
while (retryCount < maxRetries) {
|
|
85
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
86
|
+
try {
|
|
87
|
+
const txInfo = await this.tronWeb.trx.getTransactionInfo(txHash);
|
|
88
|
+
if (txInfo?.receipt) {
|
|
89
|
+
return {
|
|
90
|
+
hash: txHash,
|
|
91
|
+
status: txInfo.receipt.result === 'SUCCESS' ? 'success' : 'failed',
|
|
92
|
+
blockNumber: txInfo.blockNumber,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Continue retrying
|
|
98
|
+
}
|
|
99
|
+
retryCount++;
|
|
100
|
+
}
|
|
101
|
+
throw new Error('Transaction confirmation timeout');
|
|
102
|
+
}
|
|
103
|
+
async getTransactionReceipt(txHash) {
|
|
104
|
+
try {
|
|
105
|
+
const txInfo = await this.tronWeb.trx.getTransactionInfo(txHash);
|
|
106
|
+
if (!txInfo?.receipt) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
hash: txHash,
|
|
111
|
+
status: txInfo.receipt.result === 'SUCCESS' ? 'success' : 'failed',
|
|
112
|
+
blockNumber: txInfo.blockNumber,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async estimateGas(_from, _to, _data, _value) {
|
|
120
|
+
// Tron doesn't have gas estimation in the same way
|
|
121
|
+
// Return a default fee limit
|
|
122
|
+
return BigInt(10000000); // 10 TRX in sun
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get TronWeb instance (for advanced usage)
|
|
126
|
+
*/
|
|
127
|
+
getTronWeb() {
|
|
128
|
+
return this.tronWeb;
|
|
129
|
+
}
|
|
130
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chain type identifier
|
|
3
|
+
*/
|
|
4
|
+
export type ChainType = 'evm' | 'tron';
|
|
5
|
+
/**
|
|
6
|
+
* Chain configuration
|
|
7
|
+
*/
|
|
8
|
+
export interface ChainConfig {
|
|
9
|
+
name: string;
|
|
10
|
+
chain: string;
|
|
11
|
+
rpc_url: string;
|
|
12
|
+
rest_url: string;
|
|
13
|
+
payout_contract: string;
|
|
14
|
+
scan_url?: string;
|
|
15
|
+
tokens?: TokenConfig[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Token configuration
|
|
19
|
+
*/
|
|
20
|
+
export interface TokenConfig {
|
|
21
|
+
name: string;
|
|
22
|
+
symbol: string;
|
|
23
|
+
contract_addr: string;
|
|
24
|
+
decimal: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Balance information
|
|
28
|
+
*/
|
|
29
|
+
export interface BalanceInfo {
|
|
30
|
+
native: string;
|
|
31
|
+
tokens: Record<string, string>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Transaction options
|
|
35
|
+
*
|
|
36
|
+
* Cross-chain semantics:
|
|
37
|
+
* - EVM chains:
|
|
38
|
+
* - gasLimit / gasPrice / maxFeePerGas / maxPriorityFeePerGas are used as-is.
|
|
39
|
+
* - value is the native token amount in base units (for example, wei).
|
|
40
|
+
* - Tron:
|
|
41
|
+
* - feeLimit is used as the max TRX fee in sun.
|
|
42
|
+
* - value is ignored by the built-in TronProvider sendTransaction implementation.
|
|
43
|
+
*/
|
|
44
|
+
export interface TransactionOptions {
|
|
45
|
+
gasLimit?: bigint;
|
|
46
|
+
gasPrice?: bigint;
|
|
47
|
+
maxFeePerGas?: bigint;
|
|
48
|
+
maxPriorityFeePerGas?: bigint;
|
|
49
|
+
feeLimit?: number;
|
|
50
|
+
value?: bigint;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Transaction result
|
|
54
|
+
*/
|
|
55
|
+
export interface TransactionResult {
|
|
56
|
+
hash: string;
|
|
57
|
+
chainType: ChainType;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Transaction receipt
|
|
61
|
+
*/
|
|
62
|
+
export interface TransactionReceipt {
|
|
63
|
+
hash: string;
|
|
64
|
+
status: 'success' | 'failed';
|
|
65
|
+
blockNumber?: number;
|
|
66
|
+
blockHash?: string;
|
|
67
|
+
gasUsed?: bigint;
|
|
68
|
+
effectiveGasPrice?: bigint;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,SAAS,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@magnaflow/chain-provider",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Chain provider abstraction library for unified blockchain interactions",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:watch": "vitest",
|
|
23
|
+
"test:coverage": "vitest run --coverage",
|
|
24
|
+
"dev": "tsc --watch",
|
|
25
|
+
"clean": "rm -rf dist",
|
|
26
|
+
"prepublishOnly": "node ../../scripts/verify-package-dist.cjs"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"blockchain",
|
|
30
|
+
"chain-provider",
|
|
31
|
+
"ethereum",
|
|
32
|
+
"tron",
|
|
33
|
+
"web3",
|
|
34
|
+
"viem",
|
|
35
|
+
"tronweb"
|
|
36
|
+
],
|
|
37
|
+
"author": "",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@magnaflow/eoa-wallet": "workspace:*",
|
|
44
|
+
"tronweb": "^6.1.0",
|
|
45
|
+
"viem": "^2.39.2"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"typescript": "~5.9.3",
|
|
49
|
+
"vitest": "^2.1.9",
|
|
50
|
+
"@vitest/coverage-v8": "^2.1.9"
|
|
51
|
+
},
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/magnaflowlabs/merchant-v2-sdk.git",
|
|
55
|
+
"directory": "packages/chain-provider"
|
|
56
|
+
}
|
|
57
|
+
}
|