@heyanon/sdk 2.3.2 → 2.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/misc.d.ts +35 -0
- package/dist/adapter/transformers/toResult.d.ts +24 -0
- package/dist/adapter/types.d.ts +123 -7
- package/dist/blockchain/constants/chains.d.ts +93 -0
- package/dist/blockchain/constants/types.d.ts +33 -0
- package/dist/blockchain/evm/constants/chains.d.ts +56 -0
- package/dist/blockchain/evm/constants/misc.d.ts +69 -0
- package/dist/blockchain/evm/constants/weth9.d.ts +76 -1
- package/dist/blockchain/evm/types.d.ts +273 -0
- package/dist/blockchain/evm/utils/checkToApprove.d.ts +86 -0
- package/dist/blockchain/evm/utils/getChainFromName.d.ts +81 -0
- package/dist/blockchain/evm/utils/getChainName.d.ts +114 -0
- package/dist/blockchain/evm/utils/getUnwrapData.d.ts +114 -0
- package/dist/blockchain/evm/utils/getWrapAddress.d.ts +147 -0
- package/dist/blockchain/evm/utils/getWrapData.d.ts +189 -0
- package/dist/blockchain/evm/utils/getWrappedNative.d.ts +198 -0
- package/dist/blockchain/evm/utils/isEvmChain.d.ts +183 -0
- package/dist/blockchain/evm/utils/isNativeAddress.d.ts +221 -0
- package/dist/blockchain/evm/utils/isNativeAddress.test.d.ts +1 -0
- package/dist/blockchain/solana/types.d.ts +291 -0
- package/dist/blockchain/solana/utils/buildV0Transaction.d.ts +184 -0
- package/dist/blockchain/solana/utils/toPublicKey.d.ts +194 -0
- package/dist/blockchain/ton/types.d.ts +410 -0
- package/dist/index.js +83 -26
- package/dist/index.mjs +84 -28
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/is-address.d.ts +7 -0
- package/dist/utils/is-address.spec.d.ts +1 -0
- package/dist/utils/retry.d.ts +218 -0
- package/dist/utils/sleep.d.ts +239 -0
- package/dist/utils/stringify.d.ts +235 -0
- package/dist/utils/stringify.spec.d.ts +1 -0
- package/package.json +2 -1
|
@@ -1 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if a given chain name is an EVM-compatible blockchain
|
|
3
|
+
* @param chain - The chain name to validate
|
|
4
|
+
* @returns True if the chain is EVM-compatible, false otherwise
|
|
5
|
+
* @description This function excludes non-EVM chains like Solana and TON
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* // Valid EVM chains
|
|
9
|
+
* console.log(isEvmChain('ethereum')); // true
|
|
10
|
+
* console.log(isEvmChain('polygon')); // true
|
|
11
|
+
* console.log(isEvmChain('bsc')); // true
|
|
12
|
+
* console.log(isEvmChain('arbitrum')); // true
|
|
13
|
+
* console.log(isEvmChain('base')); // true
|
|
14
|
+
*
|
|
15
|
+
* // Non-EVM chains
|
|
16
|
+
* console.log(isEvmChain('solana')); // false
|
|
17
|
+
* console.log(isEvmChain('ton')); // false
|
|
18
|
+
*
|
|
19
|
+
* // Invalid/unknown chains
|
|
20
|
+
* console.log(isEvmChain('bitcoin')); // false
|
|
21
|
+
* console.log(isEvmChain('cosmos')); // false
|
|
22
|
+
* console.log(isEvmChain('unknown')); // false
|
|
23
|
+
*
|
|
24
|
+
* // Use in adapter function routing
|
|
25
|
+
* async function processTransaction(chain: string, transaction: any) {
|
|
26
|
+
* if (isEvmChain(chain)) {
|
|
27
|
+
* // Use EVM-specific processing
|
|
28
|
+
* return await options.evm.sendTransactions({
|
|
29
|
+
* transactions: [transaction]
|
|
30
|
+
* });
|
|
31
|
+
* } else if (chain === 'solana') {
|
|
32
|
+
* // Use Solana-specific processing
|
|
33
|
+
* return await options.solana.sendTransactions({
|
|
34
|
+
* transactions: [transaction]
|
|
35
|
+
* });
|
|
36
|
+
* } else if (chain === 'ton') {
|
|
37
|
+
* // Use TON-specific processing
|
|
38
|
+
* return await options.ton.sendTransactions({
|
|
39
|
+
* transactions: [transaction]
|
|
40
|
+
* });
|
|
41
|
+
* } else {
|
|
42
|
+
* throw new Error(`Unsupported chain: ${chain}`);
|
|
43
|
+
* }
|
|
44
|
+
* }
|
|
45
|
+
*
|
|
46
|
+
* // Validate user input
|
|
47
|
+
* function validateChainSelection(selectedChain: string) {
|
|
48
|
+
* if (!selectedChain) {
|
|
49
|
+
* return { valid: false, error: 'Chain not selected' };
|
|
50
|
+
* }
|
|
51
|
+
*
|
|
52
|
+
* if (isEvmChain(selectedChain)) {
|
|
53
|
+
* return {
|
|
54
|
+
* valid: true,
|
|
55
|
+
* type: 'evm',
|
|
56
|
+
* chainId: getChainFromName(selectedChain as EvmChain)
|
|
57
|
+
* };
|
|
58
|
+
* }
|
|
59
|
+
*
|
|
60
|
+
* if (selectedChain === 'solana' || selectedChain === 'ton') {
|
|
61
|
+
* return { valid: true, type: selectedChain };
|
|
62
|
+
* }
|
|
63
|
+
*
|
|
64
|
+
* return { valid: false, error: `Unsupported chain: ${selectedChain}` };
|
|
65
|
+
* }
|
|
66
|
+
*
|
|
67
|
+
* // Filter tokens by chain type
|
|
68
|
+
* function filterTokensByChainType(tokens: UserToken[], evmOnly: boolean = false) {
|
|
69
|
+
* if (evmOnly) {
|
|
70
|
+
* return tokens.filter(token => isEvmChain(token.chain));
|
|
71
|
+
* }
|
|
72
|
+
* return tokens;
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* // Get appropriate provider based on chain
|
|
76
|
+
* function getChainProvider(chain: string) {
|
|
77
|
+
* if (isEvmChain(chain)) {
|
|
78
|
+
* const chainId = getChainFromName(chain as EvmChain);
|
|
79
|
+
* return options.evm.getProvider(chainId);
|
|
80
|
+
* } else if (chain === 'solana') {
|
|
81
|
+
* return options.solana.getConnection();
|
|
82
|
+
* } else if (chain === 'ton') {
|
|
83
|
+
* return options.ton.getClient();
|
|
84
|
+
* } else {
|
|
85
|
+
* throw new Error(`No provider available for chain: ${chain}`);
|
|
86
|
+
* }
|
|
87
|
+
* }
|
|
88
|
+
*
|
|
89
|
+
* // Multi-chain balance fetching with type checking
|
|
90
|
+
* async function getMultiChainBalances(
|
|
91
|
+
* chains: string[],
|
|
92
|
+
* tokenAddress: string,
|
|
93
|
+
* userAddress: string
|
|
94
|
+
* ) {
|
|
95
|
+
* const balances = await Promise.allSettled(
|
|
96
|
+
* chains.map(async (chain) => {
|
|
97
|
+
* if (isEvmChain(chain)) {
|
|
98
|
+
* const provider = options.evm.getProvider(
|
|
99
|
+
* getChainFromName(chain as EvmChain)
|
|
100
|
+
* );
|
|
101
|
+
*
|
|
102
|
+
* const balance = await provider.readContract({
|
|
103
|
+
* address: tokenAddress,
|
|
104
|
+
* abi: erc20Abi,
|
|
105
|
+
* functionName: 'balanceOf',
|
|
106
|
+
* args: [userAddress]
|
|
107
|
+
* });
|
|
108
|
+
*
|
|
109
|
+
* return { chain, balance, type: 'evm' };
|
|
110
|
+
* } else {
|
|
111
|
+
* // Handle non-EVM chains differently
|
|
112
|
+
* throw new Error(`Non-EVM balance fetching not implemented for ${chain}`);
|
|
113
|
+
* }
|
|
114
|
+
* })
|
|
115
|
+
* );
|
|
116
|
+
*
|
|
117
|
+
* return balances
|
|
118
|
+
* .filter((result): result is PromiseFulfilledResult<any> => result.status === 'fulfilled')
|
|
119
|
+
* .map(result => result.value);
|
|
120
|
+
* }
|
|
121
|
+
*
|
|
122
|
+
* // Chain-specific transaction building
|
|
123
|
+
* function buildTransaction(chain: string, params: any) {
|
|
124
|
+
* if (isEvmChain(chain)) {
|
|
125
|
+
* return {
|
|
126
|
+
* target: params.to,
|
|
127
|
+
* data: params.data,
|
|
128
|
+
* value: params.value || '0'
|
|
129
|
+
* };
|
|
130
|
+
* } else if (chain === 'solana') {
|
|
131
|
+
* return {
|
|
132
|
+
* instructions: params.instructions,
|
|
133
|
+
* signers: params.signers
|
|
134
|
+
* };
|
|
135
|
+
* } else if (chain === 'ton') {
|
|
136
|
+
* return {
|
|
137
|
+
* to: params.to,
|
|
138
|
+
* amount: params.amount,
|
|
139
|
+
* payload: params.payload
|
|
140
|
+
* };
|
|
141
|
+
* } else {
|
|
142
|
+
* throw new Error(`Cannot build transaction for unsupported chain: ${chain}`);
|
|
143
|
+
* }
|
|
144
|
+
* }
|
|
145
|
+
*
|
|
146
|
+
* // UI component for chain selection
|
|
147
|
+
* function getChainCategories(chains: string[]) {
|
|
148
|
+
* const evmChains = chains.filter(isEvmChain);
|
|
149
|
+
* const nonEvmChains = chains.filter(chain => !isEvmChain(chain));
|
|
150
|
+
*
|
|
151
|
+
* return {
|
|
152
|
+
* evm: {
|
|
153
|
+
* title: 'EVM Compatible',
|
|
154
|
+
* chains: evmChains,
|
|
155
|
+
* icon: 'ethereum-logo'
|
|
156
|
+
* },
|
|
157
|
+
* nonEvm: {
|
|
158
|
+
* title: 'Other Blockchains',
|
|
159
|
+
* chains: nonEvmChains,
|
|
160
|
+
* icon: 'blockchain-logo'
|
|
161
|
+
* }
|
|
162
|
+
* };
|
|
163
|
+
* }
|
|
164
|
+
*
|
|
165
|
+
* // Type-safe chain processing
|
|
166
|
+
* function processChainSpecificOperation<T>(
|
|
167
|
+
* chain: string,
|
|
168
|
+
* evmHandler: (chain: EvmChain) => T,
|
|
169
|
+
* solanaHandler: () => T,
|
|
170
|
+
* tonHandler: () => T
|
|
171
|
+
* ): T {
|
|
172
|
+
* if (isEvmChain(chain)) {
|
|
173
|
+
* return evmHandler(chain as EvmChain);
|
|
174
|
+
* } else if (chain === 'solana') {
|
|
175
|
+
* return solanaHandler();
|
|
176
|
+
* } else if (chain === 'ton') {
|
|
177
|
+
* return tonHandler();
|
|
178
|
+
* } else {
|
|
179
|
+
* throw new Error(`Unsupported chain: ${chain}`);
|
|
180
|
+
* }
|
|
181
|
+
* }
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
1
184
|
export declare function isEvmChain(chain: string): boolean;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if the given address represents a native token (ETH, BNB, MATIC, etc.)
|
|
3
|
+
* @param address - The token address to check
|
|
4
|
+
* @returns True if the address represents a native token, false otherwise
|
|
5
|
+
* @description Uses the special NATIVE_ADDRESS constant (0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) for comparison
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* // Check native token addresses
|
|
9
|
+
* console.log(isNativeAddress('0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE')); // true
|
|
10
|
+
* console.log(isNativeAddress('0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee')); // true (case insensitive)
|
|
11
|
+
*
|
|
12
|
+
* // Check ERC20 token addresses
|
|
13
|
+
* console.log(isNativeAddress('0xA0b86a33E6417e4681831442Ff7Bd6c25b5d9C7a')); // false (USDC)
|
|
14
|
+
* console.log(isNativeAddress('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')); // false (WETH)
|
|
15
|
+
*
|
|
16
|
+
* // Use in token swap logic
|
|
17
|
+
* async function handleTokenTransfer(
|
|
18
|
+
* fromToken: string,
|
|
19
|
+
* toToken: string,
|
|
20
|
+
* amount: bigint,
|
|
21
|
+
* recipient: string
|
|
22
|
+
* ) {
|
|
23
|
+
* if (isNativeAddress(fromToken)) {
|
|
24
|
+
* // Handle native token transfer (ETH, BNB, etc.)
|
|
25
|
+
* return await options.evm.sendTransactions({
|
|
26
|
+
* transactions: [{
|
|
27
|
+
* target: recipient,
|
|
28
|
+
* value: amount,
|
|
29
|
+
* data: '0x'
|
|
30
|
+
* }]
|
|
31
|
+
* });
|
|
32
|
+
* } else {
|
|
33
|
+
* // Handle ERC20 token transfer
|
|
34
|
+
* return await options.evm.sendTransactions({
|
|
35
|
+
* transactions: [{
|
|
36
|
+
* target: fromToken,
|
|
37
|
+
* data: encodeFunctionData({
|
|
38
|
+
* abi: erc20Abi,
|
|
39
|
+
* functionName: 'transfer',
|
|
40
|
+
* args: [recipient, amount]
|
|
41
|
+
* })
|
|
42
|
+
* }]
|
|
43
|
+
* });
|
|
44
|
+
* }
|
|
45
|
+
* }
|
|
46
|
+
*
|
|
47
|
+
* // Use in DEX swap preparation
|
|
48
|
+
* function prepareSwapTransaction(
|
|
49
|
+
* tokenIn: string,
|
|
50
|
+
* tokenOut: string,
|
|
51
|
+
* amountIn: bigint,
|
|
52
|
+
* chain: EvmChain
|
|
53
|
+
* ) {
|
|
54
|
+
* const transactions: TransactionParams[] = [];
|
|
55
|
+
*
|
|
56
|
+
* // If input token is native, we might need to wrap it first
|
|
57
|
+
* if (isNativeAddress(tokenIn)) {
|
|
58
|
+
* const wrapData = getWrapData(chain, amountIn);
|
|
59
|
+
* transactions.push(wrapData);
|
|
60
|
+
* tokenIn = getWrapAddress(chain); // Update to wrapped token address
|
|
61
|
+
* } else {
|
|
62
|
+
* // Approve ERC20 token for DEX router
|
|
63
|
+
* transactions.push(...getApprovalTransactions(tokenIn, DEX_ROUTER, amountIn));
|
|
64
|
+
* }
|
|
65
|
+
*
|
|
66
|
+
* // Add swap transaction
|
|
67
|
+
* transactions.push(buildSwapTransaction(tokenIn, tokenOut, amountIn));
|
|
68
|
+
*
|
|
69
|
+
* // If output token should be native, add unwrap transaction
|
|
70
|
+
* if (isNativeAddress(tokenOut)) {
|
|
71
|
+
* const unwrapData = getUnwrapData(chain, expectedAmountOut);
|
|
72
|
+
* transactions.push(unwrapData);
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* return transactions;
|
|
76
|
+
* }
|
|
77
|
+
*
|
|
78
|
+
* // Get token balance (native vs ERC20)
|
|
79
|
+
* async function getTokenBalance(
|
|
80
|
+
* tokenAddress: string,
|
|
81
|
+
* userAddress: string,
|
|
82
|
+
* provider: PublicClient
|
|
83
|
+
* ): Promise<bigint> {
|
|
84
|
+
* if (isNativeAddress(tokenAddress)) {
|
|
85
|
+
* // Get native token balance (ETH, BNB, etc.)
|
|
86
|
+
* return await provider.getBalance({ address: userAddress });
|
|
87
|
+
* } else {
|
|
88
|
+
* // Get ERC20 token balance
|
|
89
|
+
* return await provider.readContract({
|
|
90
|
+
* address: tokenAddress,
|
|
91
|
+
* abi: erc20Abi,
|
|
92
|
+
* functionName: 'balanceOf',
|
|
93
|
+
* args: [userAddress]
|
|
94
|
+
* });
|
|
95
|
+
* }
|
|
96
|
+
* }
|
|
97
|
+
*
|
|
98
|
+
* // Token validation for UI
|
|
99
|
+
* function validateTokenAddress(address: string): {
|
|
100
|
+
* valid: boolean;
|
|
101
|
+
* type: 'native' | 'erc20' | 'invalid';
|
|
102
|
+
* message?: string;
|
|
103
|
+
* } {
|
|
104
|
+
* if (!address) {
|
|
105
|
+
* return { valid: false, type: 'invalid', message: 'Address is required' };
|
|
106
|
+
* }
|
|
107
|
+
*
|
|
108
|
+
* if (isNativeAddress(address)) {
|
|
109
|
+
* return { valid: true, type: 'native' };
|
|
110
|
+
* }
|
|
111
|
+
*
|
|
112
|
+
* if (isAddress(address)) {
|
|
113
|
+
* return { valid: true, type: 'erc20' };
|
|
114
|
+
* }
|
|
115
|
+
*
|
|
116
|
+
* return { valid: false, type: 'invalid', message: 'Invalid address format' };
|
|
117
|
+
* }
|
|
118
|
+
*
|
|
119
|
+
* // Portfolio calculation with native tokens
|
|
120
|
+
* async function calculatePortfolioValue(
|
|
121
|
+
* tokens: Array<{ address: string; amount: bigint }>,
|
|
122
|
+
* chain: EvmChain
|
|
123
|
+
* ) {
|
|
124
|
+
* const provider = options.evm.getProvider(getChainFromName(chain));
|
|
125
|
+
*
|
|
126
|
+
* const tokenValues = await Promise.all(
|
|
127
|
+
* tokens.map(async ({ address, amount }) => {
|
|
128
|
+
* let tokenSymbol: string;
|
|
129
|
+
* let tokenDecimals: number;
|
|
130
|
+
*
|
|
131
|
+
* if (isNativeAddress(address)) {
|
|
132
|
+
* // Handle native token
|
|
133
|
+
* tokenSymbol = getNativeTokenSymbol(chain); // ETH, BNB, MATIC, etc.
|
|
134
|
+
* tokenDecimals = 18;
|
|
135
|
+
* } else {
|
|
136
|
+
* // Handle ERC20 token
|
|
137
|
+
* [tokenSymbol, tokenDecimals] = await Promise.all([
|
|
138
|
+
* provider.readContract({
|
|
139
|
+
* address: address,
|
|
140
|
+
* abi: erc20Abi,
|
|
141
|
+
* functionName: 'symbol'
|
|
142
|
+
* }),
|
|
143
|
+
* provider.readContract({
|
|
144
|
+
* address: address,
|
|
145
|
+
* abi: erc20Abi,
|
|
146
|
+
* functionName: 'decimals'
|
|
147
|
+
* })
|
|
148
|
+
* ]);
|
|
149
|
+
* }
|
|
150
|
+
*
|
|
151
|
+
* const formattedAmount = formatUnits(amount, tokenDecimals);
|
|
152
|
+
* const usdValue = await getTokenPriceUSD(tokenSymbol);
|
|
153
|
+
*
|
|
154
|
+
* return {
|
|
155
|
+
* address,
|
|
156
|
+
* symbol: tokenSymbol,
|
|
157
|
+
* amount: formattedAmount,
|
|
158
|
+
* usdValue: parseFloat(formattedAmount) * usdValue,
|
|
159
|
+
* isNative: isNativeAddress(address)
|
|
160
|
+
* };
|
|
161
|
+
* })
|
|
162
|
+
* );
|
|
163
|
+
*
|
|
164
|
+
* return tokenValues;
|
|
165
|
+
* }
|
|
166
|
+
*
|
|
167
|
+
* // Gas estimation for different token types
|
|
168
|
+
* async function estimateTransferGas(
|
|
169
|
+
* tokenAddress: string,
|
|
170
|
+
* to: string,
|
|
171
|
+
* amount: bigint,
|
|
172
|
+
* from: string,
|
|
173
|
+
* provider: PublicClient
|
|
174
|
+
* ): Promise<bigint> {
|
|
175
|
+
* if (isNativeAddress(tokenAddress)) {
|
|
176
|
+
* // Estimate gas for native token transfer
|
|
177
|
+
* return await provider.estimateGas({
|
|
178
|
+
* account: from,
|
|
179
|
+
* to,
|
|
180
|
+
* value: amount
|
|
181
|
+
* });
|
|
182
|
+
* } else {
|
|
183
|
+
* // Estimate gas for ERC20 transfer
|
|
184
|
+
* return await provider.estimateGas({
|
|
185
|
+
* account: from,
|
|
186
|
+
* to: tokenAddress,
|
|
187
|
+
* data: encodeFunctionData({
|
|
188
|
+
* abi: erc20Abi,
|
|
189
|
+
* functionName: 'transfer',
|
|
190
|
+
* args: [to, amount]
|
|
191
|
+
* })
|
|
192
|
+
* });
|
|
193
|
+
* }
|
|
194
|
+
* }
|
|
195
|
+
*
|
|
196
|
+
* // Filter tokens by type
|
|
197
|
+
* function filterTokensByType(
|
|
198
|
+
* tokens: UserToken[],
|
|
199
|
+
* includeNative: boolean = true,
|
|
200
|
+
* includeERC20: boolean = true
|
|
201
|
+
* ) {
|
|
202
|
+
* return tokens.filter(token => {
|
|
203
|
+
* const isNative = isNativeAddress(token.address);
|
|
204
|
+
* return (includeNative && isNative) || (includeERC20 && !isNative);
|
|
205
|
+
* });
|
|
206
|
+
* }
|
|
207
|
+
*
|
|
208
|
+
* // Transaction fee calculation
|
|
209
|
+
* function calculateTransactionCost(
|
|
210
|
+
* tokenAddress: string,
|
|
211
|
+
* gasPrice: bigint,
|
|
212
|
+
* gasLimit: bigint
|
|
213
|
+
* ): { gasCost: bigint; requiresNativeForGas: boolean } {
|
|
214
|
+
* const gasCost = gasPrice * gasLimit;
|
|
215
|
+
* const requiresNativeForGas = !isNativeAddress(tokenAddress);
|
|
216
|
+
*
|
|
217
|
+
* return { gasCost, requiresNativeForGas };
|
|
218
|
+
* }
|
|
219
|
+
* ```
|
|
220
|
+
*/
|
|
221
|
+
export declare function isNativeAddress(address: string): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,16 +1,307 @@
|
|
|
1
1
|
import { PublicKey, TransactionSignature, VersionedTransaction, Transaction } from '@solana/web3.js';
|
|
2
|
+
/**
|
|
3
|
+
* Data returned for each executed Solana transaction
|
|
4
|
+
* @interface TransactionReturnData
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* // Successful transaction result
|
|
8
|
+
* const successData: TransactionReturnData = {
|
|
9
|
+
* message: "Transaction confirmed",
|
|
10
|
+
* hash: "2ZE7R7Xhm9V8Qf4vKyRKMJ6XHKM6eRXmFw7P8NXTW7qTgJhG5jzYR4M8KKvxjJb9Vs6KfYQpYsKJ4JUhP5K"
|
|
11
|
+
* };
|
|
12
|
+
*
|
|
13
|
+
* // Failed transaction result
|
|
14
|
+
* const failedData: TransactionReturnData = {
|
|
15
|
+
* message: "Transaction failed: Insufficient funds",
|
|
16
|
+
* hash: "3AF8S8Ymi0W9Rg5wLzSKNK7YILM7fSYnGx8Q9OYUX8rUhKiH6kzZS5N9LLwyKc0Wr7LgZRqQtLJ5KViQ6L"
|
|
17
|
+
* };
|
|
18
|
+
*
|
|
19
|
+
* // Process transaction result
|
|
20
|
+
* function handleTransactionResult(result: TransactionReturnData) {
|
|
21
|
+
* console.log(`Status: ${result.message}`);
|
|
22
|
+
* console.log(`Transaction: https://solscan.io/tx/${result.hash}`);
|
|
23
|
+
*
|
|
24
|
+
* if (result.message.includes('confirmed')) {
|
|
25
|
+
* // Handle success
|
|
26
|
+
* showSuccessNotification(result.hash);
|
|
27
|
+
* } else {
|
|
28
|
+
* // Handle error
|
|
29
|
+
* showErrorNotification(result.message);
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
2
34
|
export interface TransactionReturnData {
|
|
35
|
+
/** Status message or error description */
|
|
3
36
|
readonly message: string;
|
|
37
|
+
/** Transaction signature/hash on Solana blockchain */
|
|
4
38
|
readonly hash: TransactionSignature;
|
|
5
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Complete result of Solana transaction execution containing all transaction results
|
|
42
|
+
* @interface TransactionReturn
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* // Batch transaction result
|
|
46
|
+
* const batchResult: TransactionReturn = {
|
|
47
|
+
* data: [
|
|
48
|
+
* {
|
|
49
|
+
* message: "Token transfer confirmed",
|
|
50
|
+
* hash: "2ZE7R7Xhm9V8Qf4vKyRKMJ6XHKM6eRXmFw7P8NXTW7qTgJhG5jzYR4M8KKvxjJb9Vs6KfYQpYsKJ4JUhP5K"
|
|
51
|
+
* },
|
|
52
|
+
* {
|
|
53
|
+
* message: "Swap completed",
|
|
54
|
+
* hash: "3AF8S8Ymi0W9Rg5wLzSKNK7YILM7fSYnGx8Q9OYUX8rUhKiH6kzZS5N9LLwyKc0Wr7LgZRqQtLJ5KViQ6L"
|
|
55
|
+
* }
|
|
56
|
+
* ]
|
|
57
|
+
* };
|
|
58
|
+
*
|
|
59
|
+
* // Single transaction result
|
|
60
|
+
* const singleResult: TransactionReturn = {
|
|
61
|
+
* data: [{
|
|
62
|
+
* message: "SOL transfer successful",
|
|
63
|
+
* hash: "4BG9T9Znj1X0Sg6xMzTKOL8YJMN8gTZoHy9R0PZVY9sVjLjI7l0aT6O0MMxzLd1Xs8MhaSrRuMK6LWjR7M"
|
|
64
|
+
* }]
|
|
65
|
+
* };
|
|
66
|
+
*
|
|
67
|
+
* // Process all results
|
|
68
|
+
* function processBatchResults(result: TransactionReturn) {
|
|
69
|
+
* const successful = result.data.filter(tx =>
|
|
70
|
+
* tx.message.includes('confirmed') || tx.message.includes('successful')
|
|
71
|
+
* );
|
|
72
|
+
*
|
|
73
|
+
* const failed = result.data.filter(tx =>
|
|
74
|
+
* tx.message.includes('failed') || tx.message.includes('error')
|
|
75
|
+
* );
|
|
76
|
+
*
|
|
77
|
+
* console.log(`Successful: ${successful.length}, Failed: ${failed.length}`);
|
|
78
|
+
*
|
|
79
|
+
* // Create summary for user
|
|
80
|
+
* return {
|
|
81
|
+
* totalTransactions: result.data.length,
|
|
82
|
+
* successCount: successful.length,
|
|
83
|
+
* failureCount: failed.length,
|
|
84
|
+
* successfulHashes: successful.map(tx => tx.hash),
|
|
85
|
+
* failedHashes: failed.map(tx => tx.hash)
|
|
86
|
+
* };
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
6
90
|
export interface TransactionReturn {
|
|
91
|
+
/** Array of transaction results */
|
|
7
92
|
readonly data: TransactionReturnData[];
|
|
8
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Properties for sending one or multiple Solana transactions
|
|
96
|
+
* @interface SendTransactionProps
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
99
|
+
* // Single transaction
|
|
100
|
+
* const transferProps: SendTransactionProps = {
|
|
101
|
+
* account: toPublicKey('11111111111111111111111111111112'),
|
|
102
|
+
* transactions: [await buildV0Transaction({
|
|
103
|
+
* connection: options.solana.getConnection(),
|
|
104
|
+
* payer: userPublicKey,
|
|
105
|
+
* instructions: [SystemProgram.transfer({
|
|
106
|
+
* fromPubkey: userPublicKey,
|
|
107
|
+
* toPubkey: recipientPublicKey,
|
|
108
|
+
* lamports: 0.1 * LAMPORTS_PER_SOL
|
|
109
|
+
* })]
|
|
110
|
+
* })]
|
|
111
|
+
* };
|
|
112
|
+
*
|
|
113
|
+
* // Multiple transactions (batch operations)
|
|
114
|
+
* const batchProps: SendTransactionProps = {
|
|
115
|
+
* account: userPublicKey,
|
|
116
|
+
* transactions: [
|
|
117
|
+
* await buildV0Transaction({
|
|
118
|
+
* connection: options.solana.getConnection(),
|
|
119
|
+
* payer: userPublicKey,
|
|
120
|
+
* instructions: [createAssociatedTokenAccountInstruction(
|
|
121
|
+
* userPublicKey, // payer
|
|
122
|
+
* tokenAccount, // associated token account
|
|
123
|
+
* userPublicKey, // owner
|
|
124
|
+
* mintAddress // mint
|
|
125
|
+
* )]
|
|
126
|
+
* }),
|
|
127
|
+
* await buildV0Transaction({
|
|
128
|
+
* connection: options.solana.getConnection(),
|
|
129
|
+
* payer: userPublicKey,
|
|
130
|
+
* instructions: [createTransferInstruction(
|
|
131
|
+
* fromTokenAccount,
|
|
132
|
+
* toTokenAccount,
|
|
133
|
+
* userPublicKey,
|
|
134
|
+
* 100_000 // 0.1 USDC
|
|
135
|
+
* )]
|
|
136
|
+
* })
|
|
137
|
+
* ]
|
|
138
|
+
* };
|
|
139
|
+
*
|
|
140
|
+
* // Jupiter swap transaction
|
|
141
|
+
* const swapProps: SendTransactionProps = {
|
|
142
|
+
* account: await options.solana.getPublicKey(),
|
|
143
|
+
* transactions: [await buildV0Transaction({
|
|
144
|
+
* connection: options.solana.getConnection(),
|
|
145
|
+
* payer: await options.solana.getPublicKey(),
|
|
146
|
+
* instructions: await getJupiterSwapInstructions({
|
|
147
|
+
* inputMint: 'So11111111111111111111111111111111111111112', // SOL
|
|
148
|
+
* outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
|
|
149
|
+
* amount: 0.1 * LAMPORTS_PER_SOL,
|
|
150
|
+
* slippageBps: 50
|
|
151
|
+
* })
|
|
152
|
+
* })]
|
|
153
|
+
* };
|
|
154
|
+
*
|
|
155
|
+
* // Usage in adapter function
|
|
156
|
+
* async function executeSolanaTransactions(props: SendTransactionProps) {
|
|
157
|
+
* const result = await options.solana.sendTransactions(props);
|
|
158
|
+
*
|
|
159
|
+
* // Log all transaction hashes
|
|
160
|
+
* result.data.forEach((tx, index) => {
|
|
161
|
+
* console.log(`Transaction ${index + 1}: ${tx.hash}`);
|
|
162
|
+
* });
|
|
163
|
+
*
|
|
164
|
+
* return result;
|
|
165
|
+
* }
|
|
166
|
+
*
|
|
167
|
+
* // Error handling wrapper
|
|
168
|
+
* async function safeSendTransactions(props: SendTransactionProps) {
|
|
169
|
+
* try {
|
|
170
|
+
* return await options.solana.sendTransactions(props);
|
|
171
|
+
* } catch (error) {
|
|
172
|
+
* console.error('Failed to send transactions:', error);
|
|
173
|
+
* return {
|
|
174
|
+
* data: props.transactions.map(() => ({
|
|
175
|
+
* message: `Transaction failed: ${error.message}`,
|
|
176
|
+
* hash: '' as TransactionSignature
|
|
177
|
+
* }))
|
|
178
|
+
* };
|
|
179
|
+
* }
|
|
180
|
+
* }
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
9
183
|
export interface SendTransactionProps {
|
|
184
|
+
/** Account that will execute the transactions */
|
|
10
185
|
readonly account: PublicKey;
|
|
186
|
+
/** Array of versioned transactions to execute */
|
|
11
187
|
readonly transactions: VersionedTransaction[];
|
|
12
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Properties for signing Solana transactions
|
|
191
|
+
* @interface SignTransactionProps
|
|
192
|
+
* @example
|
|
193
|
+
* ```typescript
|
|
194
|
+
* // Sign a versioned transaction (default)
|
|
195
|
+
* const signProps: SignTransactionProps = {
|
|
196
|
+
* transaction: await buildV0Transaction({
|
|
197
|
+
* connection: options.solana.getConnection(),
|
|
198
|
+
* payer: userPublicKey,
|
|
199
|
+
* instructions: [transferInstruction]
|
|
200
|
+
* })
|
|
201
|
+
* };
|
|
202
|
+
*
|
|
203
|
+
* // Sign with specific method
|
|
204
|
+
* const partialSignProps: SignTransactionProps = {
|
|
205
|
+
* transaction: versionedTransaction,
|
|
206
|
+
* signName: 'partialSign' // For multi-sig scenarios
|
|
207
|
+
* };
|
|
208
|
+
*
|
|
209
|
+
* // Sign legacy transaction
|
|
210
|
+
* const legacySignProps: SignTransactionProps = {
|
|
211
|
+
* transaction: new Transaction().add(transferInstruction),
|
|
212
|
+
* signName: 'sign'
|
|
213
|
+
* };
|
|
214
|
+
*
|
|
215
|
+
* // Multi-signature workflow
|
|
216
|
+
* async function multiSigTransactionFlow(
|
|
217
|
+
* transaction: VersionedTransaction,
|
|
218
|
+
* signers: PublicKey[]
|
|
219
|
+
* ) {
|
|
220
|
+
* const signatures: (VersionedTransaction | Transaction)[] = [];
|
|
221
|
+
*
|
|
222
|
+
* // Each signer partially signs the transaction
|
|
223
|
+
* for (const signer of signers) {
|
|
224
|
+
* const signedTx = await options.solana.signTransactions([{
|
|
225
|
+
* transaction,
|
|
226
|
+
* signName: 'partialSign'
|
|
227
|
+
* }]);
|
|
228
|
+
*
|
|
229
|
+
* signatures.push(signedTx[0]);
|
|
230
|
+
* }
|
|
231
|
+
*
|
|
232
|
+
* return signatures;
|
|
233
|
+
* }
|
|
234
|
+
*
|
|
235
|
+
* // Batch signing
|
|
236
|
+
* async function signMultipleTransactions(
|
|
237
|
+
* transactions: (VersionedTransaction | Transaction)[]
|
|
238
|
+
* ) {
|
|
239
|
+
* const signProps = transactions.map(tx => ({
|
|
240
|
+
* transaction: tx,
|
|
241
|
+
* signName: 'sign' as const
|
|
242
|
+
* }));
|
|
243
|
+
*
|
|
244
|
+
* return await options.solana.signTransactions(signProps);
|
|
245
|
+
* }
|
|
246
|
+
*
|
|
247
|
+
* // Conditional signing based on transaction type
|
|
248
|
+
* function createSignProps(
|
|
249
|
+
* transaction: VersionedTransaction | Transaction,
|
|
250
|
+
* isMultiSig: boolean = false
|
|
251
|
+
* ): SignTransactionProps {
|
|
252
|
+
* return {
|
|
253
|
+
* transaction,
|
|
254
|
+
* signName: isMultiSig ? 'partialSign' : 'sign'
|
|
255
|
+
* };
|
|
256
|
+
* }
|
|
257
|
+
*
|
|
258
|
+
* // Prepare transaction for signing
|
|
259
|
+
* async function prepareAndSignTransaction(
|
|
260
|
+
* instructions: TransactionInstruction[],
|
|
261
|
+
* requiresPartialSign: boolean = false
|
|
262
|
+
* ) {
|
|
263
|
+
* const transaction = await buildV0Transaction({
|
|
264
|
+
* connection: options.solana.getConnection(),
|
|
265
|
+
* payer: await options.solana.getPublicKey(),
|
|
266
|
+
* instructions
|
|
267
|
+
* });
|
|
268
|
+
*
|
|
269
|
+
* const signProps: SignTransactionProps = {
|
|
270
|
+
* transaction,
|
|
271
|
+
* signName: requiresPartialSign ? 'partialSign' : 'sign'
|
|
272
|
+
* };
|
|
273
|
+
*
|
|
274
|
+
* const [signedTransaction] = await options.solana.signTransactions([signProps]);
|
|
275
|
+
* return signedTransaction;
|
|
276
|
+
* }
|
|
277
|
+
*
|
|
278
|
+
* // Integration with wallet adapters
|
|
279
|
+
* async function signTransactionWithWallet(
|
|
280
|
+
* transaction: VersionedTransaction,
|
|
281
|
+
* walletType: 'phantom' | 'solflare' | 'backpack'
|
|
282
|
+
* ) {
|
|
283
|
+
* const signProps: SignTransactionProps = {
|
|
284
|
+
* transaction,
|
|
285
|
+
* signName: 'sign'
|
|
286
|
+
* };
|
|
287
|
+
*
|
|
288
|
+
* // Different wallets might have different signing patterns
|
|
289
|
+
* switch (walletType) {
|
|
290
|
+
* case 'phantom':
|
|
291
|
+
* return await options.solana.signTransactions([signProps]);
|
|
292
|
+
* case 'solflare':
|
|
293
|
+
* return await options.solana.signTransactions([signProps]);
|
|
294
|
+
* case 'backpack':
|
|
295
|
+
* return await options.solana.signTransactions([signProps]);
|
|
296
|
+
* default:
|
|
297
|
+
* throw new Error(`Unsupported wallet: ${walletType}`);
|
|
298
|
+
* }
|
|
299
|
+
* }
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
13
302
|
export interface SignTransactionProps {
|
|
303
|
+
/** Transaction to sign (versioned or legacy) */
|
|
14
304
|
readonly transaction: VersionedTransaction | Transaction;
|
|
305
|
+
/** Signing method - 'sign' for full signature, 'partialSign' for multi-sig scenarios */
|
|
15
306
|
readonly signName?: 'sign' | 'partialSign';
|
|
16
307
|
}
|