@tonappchain/sdk 0.7.0-rc24-test-8 → 0.7.0-rc24-test-10
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/artifacts/dev/tac/artifacts.js +2 -2
- package/dist/artifacts/dev/tac/wrappers.d.ts +4 -4
- package/dist/artifacts/mainnet/tac/artifacts.js +2 -2
- package/dist/artifacts/mainnet/tac/wrappers.d.ts +4 -4
- package/dist/artifacts/tacTypes.d.ts +1 -1
- package/dist/artifacts/testnet/tac/artifacts.js +2 -2
- package/dist/artifacts/testnet/tac/wrappers.d.ts +4 -4
- package/dist/artifacts/tonTypes.d.ts +1 -1
- package/dist/src/adapters/contractOpener.js +3 -1
- package/dist/src/agnosticSdk/AbiHandler.d.ts +23 -0
- package/dist/src/agnosticSdk/AbiHandler.js +105 -0
- package/dist/src/agnosticSdk/AgnosticSdk.d.ts +47 -217
- package/dist/src/agnosticSdk/AgnosticSdk.js +60 -595
- package/dist/src/agnosticSdk/AgnosticStructs.d.ts +108 -0
- package/dist/src/agnosticSdk/AgnosticStructs.js +23 -0
- package/dist/src/agnosticSdk/DebugHelpers.d.ts +88 -0
- package/dist/src/agnosticSdk/DebugHelpers.js +274 -0
- package/dist/src/agnosticSdk/HooksHandler.d.ts +43 -0
- package/dist/src/agnosticSdk/HooksHandler.js +102 -0
- package/dist/src/agnosticSdk/ReplacementHelper.d.ts +95 -0
- package/dist/src/agnosticSdk/ReplacementHelper.js +233 -0
- package/dist/src/assets/NFT.js +3 -3
- package/dist/src/errors/index.d.ts +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.js +26 -2
- package/dist/src/sdk/Configuration.js +5 -5
- package/dist/src/sdk/OperationTracker.js +37 -14
- package/dist/src/sdk/TONTransactionManager.js +3 -2
- package/dist/src/sdk/TacSdk.js +4 -37
- package/dist/src/structs/InternalStruct.d.ts +1 -1
- package/dist/src/wrappers/ContentUtils.js +0 -1
- package/package.json +1 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NFTData is a struct that contains the nft, id, and amount of the nft
|
|
3
|
+
* @param nft - The address of the nft
|
|
4
|
+
* @param id - The id of the nft
|
|
5
|
+
* @param amount - The amount of the nft to transfer(for ERC1155, but currently supported only ERC721)
|
|
6
|
+
*/
|
|
7
|
+
export interface NFTData {
|
|
8
|
+
nft: string;
|
|
9
|
+
id: bigint;
|
|
10
|
+
amount: bigint;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* BridgeData is a struct that contains the tokens, nfts, and isRequired of the bridge data
|
|
14
|
+
* @param tokens - The addresses of the tokens to bridge
|
|
15
|
+
* @param nfts - The nfts to bridge
|
|
16
|
+
* @param isRequired - Whether the bridge is required
|
|
17
|
+
*/
|
|
18
|
+
export interface BridgeData {
|
|
19
|
+
tokens: string[];
|
|
20
|
+
nfts: NFTData[];
|
|
21
|
+
isRequired: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* HookType is an enum that contains the type of the hook
|
|
25
|
+
* @param Custom - The custom hook
|
|
26
|
+
* @param FullBalanceApprove - The full balance approve hook
|
|
27
|
+
* @param FullBalanceTransfer - The full balance transfer hook
|
|
28
|
+
*/
|
|
29
|
+
export declare enum HookType {
|
|
30
|
+
Custom = 0,
|
|
31
|
+
FullBalanceApprove = 1,
|
|
32
|
+
FullBalanceTransfer = 2
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* ReplacementType is an enum that contains the type of the replacement
|
|
36
|
+
* @param Amount - The amount replacement
|
|
37
|
+
*/
|
|
38
|
+
export declare enum ReplacementType {
|
|
39
|
+
Amount = 0
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* AmountChange is a struct that contains the position, length, token, and balance address of the amount change
|
|
43
|
+
* @param position - The position of the amount change(position of the parameter in the function call)
|
|
44
|
+
* @param len - The length of the amount change(length of the parameter in the function call)
|
|
45
|
+
* @param token - The token of the amount change
|
|
46
|
+
* @param balanceAddress - The balance address of the amount change(address to check balance for)
|
|
47
|
+
*/
|
|
48
|
+
export interface AmountChange {
|
|
49
|
+
position: number;
|
|
50
|
+
len: number;
|
|
51
|
+
token: string;
|
|
52
|
+
balanceAddress: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* CustomHookData is a struct that contains the isFromSAPerspective, contractAddress, value, data, and improvedMissionInfo of the custom hook
|
|
56
|
+
* @param isFromSAPerspective - Whether the hook is from the smart account perspective or from proxy perspective
|
|
57
|
+
* @param contractAddress - The address of the contract to call
|
|
58
|
+
* @param value - The value of the hook
|
|
59
|
+
* @param data - The data of the hook
|
|
60
|
+
* @param improvedMissionInfo - The improved mission info of the hook
|
|
61
|
+
*/
|
|
62
|
+
export interface CustomHookData {
|
|
63
|
+
isFromSAPerspective: boolean;
|
|
64
|
+
contractAddress: string;
|
|
65
|
+
value: bigint;
|
|
66
|
+
data: string;
|
|
67
|
+
improvedMissionInfo: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* ApproveHookData is a struct that contains the token, to, and isFromSAPerspective of the approve hook
|
|
71
|
+
* @param token - The token to approve
|
|
72
|
+
* @param to - The address to approve to
|
|
73
|
+
* @param isFromSAPerspective - Whether the hook is from the smart account perspective or from proxy perspective
|
|
74
|
+
*/
|
|
75
|
+
export interface ApproveHookData {
|
|
76
|
+
token: string;
|
|
77
|
+
to: string;
|
|
78
|
+
isFromSAPerspective: boolean;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* TransferHookData is a struct that contains the token, to, and isFromSAPerspective of the transfer hook
|
|
82
|
+
* @param token - The token to transfer
|
|
83
|
+
* @param to - The address to transfer to
|
|
84
|
+
* @param isFromSAPerspective - Whether the hook is from the smart account perspective or from proxy perspective
|
|
85
|
+
*/
|
|
86
|
+
export interface TransferHookData {
|
|
87
|
+
token: string;
|
|
88
|
+
to: string;
|
|
89
|
+
isFromSAPerspective: boolean;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Hook is a struct that contains the hookType and hookData of the hook
|
|
93
|
+
* @param hookType - The type of the hook
|
|
94
|
+
* @param hookData - The data of the hook
|
|
95
|
+
*/
|
|
96
|
+
export interface Hook {
|
|
97
|
+
hookType: HookType;
|
|
98
|
+
hookData: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* ZapCall is a struct that contains the hooks and bridgeData of the zap call
|
|
102
|
+
* @param hooks - The hooks of the zap call
|
|
103
|
+
* @param bridgeData - The bridge data of the zap call
|
|
104
|
+
*/
|
|
105
|
+
export interface ZapCall {
|
|
106
|
+
hooks: Hook[];
|
|
107
|
+
bridgeData: BridgeData;
|
|
108
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ReplacementType = exports.HookType = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* HookType is an enum that contains the type of the hook
|
|
6
|
+
* @param Custom - The custom hook
|
|
7
|
+
* @param FullBalanceApprove - The full balance approve hook
|
|
8
|
+
* @param FullBalanceTransfer - The full balance transfer hook
|
|
9
|
+
*/
|
|
10
|
+
var HookType;
|
|
11
|
+
(function (HookType) {
|
|
12
|
+
HookType[HookType["Custom"] = 0] = "Custom";
|
|
13
|
+
HookType[HookType["FullBalanceApprove"] = 1] = "FullBalanceApprove";
|
|
14
|
+
HookType[HookType["FullBalanceTransfer"] = 2] = "FullBalanceTransfer";
|
|
15
|
+
})(HookType || (exports.HookType = HookType = {}));
|
|
16
|
+
/**
|
|
17
|
+
* ReplacementType is an enum that contains the type of the replacement
|
|
18
|
+
* @param Amount - The amount replacement
|
|
19
|
+
*/
|
|
20
|
+
var ReplacementType;
|
|
21
|
+
(function (ReplacementType) {
|
|
22
|
+
ReplacementType[ReplacementType["Amount"] = 0] = "Amount";
|
|
23
|
+
})(ReplacementType || (exports.ReplacementType = ReplacementType = {}));
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Interface } from 'ethers';
|
|
2
|
+
import { Hook, NFTData, ZapCall } from './AgnosticStructs';
|
|
3
|
+
export declare class DebugHelpers {
|
|
4
|
+
/**
|
|
5
|
+
* Build a complete ZapCall
|
|
6
|
+
* @param hooks - The hooks of the zap call
|
|
7
|
+
* @param bridgeTokens - The tokens to bridge
|
|
8
|
+
* @param bridgeNFTs - The nfts to bridge
|
|
9
|
+
* @returns The zap call
|
|
10
|
+
*/
|
|
11
|
+
buildZapCall(hooks: Hook[], bridgeTokens?: string[], bridgeNFTs?: NFTData[]): ZapCall;
|
|
12
|
+
/**
|
|
13
|
+
* Debug helper: Decode hook data back to readable format
|
|
14
|
+
* @param hook - The hook to decode
|
|
15
|
+
* @returns The decoded hook
|
|
16
|
+
*/
|
|
17
|
+
decodeHookData(hook: Hook): any;
|
|
18
|
+
/**
|
|
19
|
+
* Debug helper: Get estimated gas for a ZapCall
|
|
20
|
+
* @param zapCall - The zap call to estimate the gas usage for
|
|
21
|
+
* @returns The estimated gas usage
|
|
22
|
+
*/
|
|
23
|
+
estimateGasUsage(zapCall: ZapCall): number;
|
|
24
|
+
/**
|
|
25
|
+
* Visualize ZapCall chain - Human readable description of all operations
|
|
26
|
+
* @param zapCall - The zap call to visualize
|
|
27
|
+
*/
|
|
28
|
+
visualizeZapCall(zapCall: ZapCall, contractInterfaces: Map<string, Interface>): void;
|
|
29
|
+
/**
|
|
30
|
+
* Private helper to describe individual hooks
|
|
31
|
+
* @param hook - The hook to describe
|
|
32
|
+
* @returns The description of the hook
|
|
33
|
+
*/
|
|
34
|
+
private _describeHook;
|
|
35
|
+
/**
|
|
36
|
+
* Describe custom hook with function details
|
|
37
|
+
* @param hook - The hook to describe
|
|
38
|
+
* @returns The description of the hook
|
|
39
|
+
*/
|
|
40
|
+
private _describeCustomHook;
|
|
41
|
+
/**
|
|
42
|
+
* Describe approve hook
|
|
43
|
+
* @param hook - The hook to describe
|
|
44
|
+
* @returns The description of the hook
|
|
45
|
+
*/
|
|
46
|
+
private _describeApproveHook;
|
|
47
|
+
/**
|
|
48
|
+
* Describe transfer hook
|
|
49
|
+
* @param hook - The hook to describe
|
|
50
|
+
* @returns The description of the hook
|
|
51
|
+
*/
|
|
52
|
+
private _describeTransferHook;
|
|
53
|
+
/**
|
|
54
|
+
* Format address for display (show first 6 and last 4 characters)
|
|
55
|
+
* @param address - The address to format
|
|
56
|
+
* @returns The formatted address
|
|
57
|
+
*/
|
|
58
|
+
private _formatAddress;
|
|
59
|
+
/**
|
|
60
|
+
* Get a detailed breakdown of a ZapCall for logging
|
|
61
|
+
* @param zapCall - The zap call to get the breakdown for
|
|
62
|
+
* @returns The breakdown of the zap call
|
|
63
|
+
*/
|
|
64
|
+
getZapCallBreakdown(zapCall: ZapCall, contractInterfaces: Map<string, Interface>): {
|
|
65
|
+
totalHooks: number;
|
|
66
|
+
hookTypes: {
|
|
67
|
+
[key: string]: number;
|
|
68
|
+
};
|
|
69
|
+
gasEstimate: number;
|
|
70
|
+
encodedSize: number;
|
|
71
|
+
bridgeRequired: boolean;
|
|
72
|
+
hookDescriptions: string[];
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Compare two ZapCalls and show differences
|
|
76
|
+
* @param zapCall1 - The first zap call to compare
|
|
77
|
+
* @param zapCall2 - The second zap call to compare
|
|
78
|
+
* @param label1 - The label of the first zap call
|
|
79
|
+
* @param label2 - The label of the second zap call
|
|
80
|
+
*/
|
|
81
|
+
compareZapCalls(zapCall1: ZapCall, zapCall2: ZapCall, label1: string | undefined, label2: string | undefined, contractInterfaces: Map<string, Interface>): void;
|
|
82
|
+
/**
|
|
83
|
+
* Encode ZapCall for transaction
|
|
84
|
+
* @param zapCall - The zap call to encode
|
|
85
|
+
* @returns The encoded zap call that can be used as calldata in tac sdk
|
|
86
|
+
*/
|
|
87
|
+
encodeZapCall(zapCall: ZapCall): string;
|
|
88
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DebugHelpers = void 0;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
const AgnosticStructs_1 = require("./AgnosticStructs");
|
|
6
|
+
class DebugHelpers {
|
|
7
|
+
/**
|
|
8
|
+
* Build a complete ZapCall
|
|
9
|
+
* @param hooks - The hooks of the zap call
|
|
10
|
+
* @param bridgeTokens - The tokens to bridge
|
|
11
|
+
* @param bridgeNFTs - The nfts to bridge
|
|
12
|
+
* @returns The zap call
|
|
13
|
+
*/
|
|
14
|
+
buildZapCall(hooks, bridgeTokens = [], bridgeNFTs = []) {
|
|
15
|
+
return {
|
|
16
|
+
hooks,
|
|
17
|
+
bridgeData: {
|
|
18
|
+
tokens: bridgeTokens,
|
|
19
|
+
nfts: bridgeNFTs,
|
|
20
|
+
isRequired: bridgeTokens.length > 0 || bridgeNFTs.length > 0 ? true : false,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Debug helper: Decode hook data back to readable format
|
|
26
|
+
* @param hook - The hook to decode
|
|
27
|
+
* @returns The decoded hook
|
|
28
|
+
*/
|
|
29
|
+
decodeHookData(hook) {
|
|
30
|
+
try {
|
|
31
|
+
switch (hook.hookType) {
|
|
32
|
+
case AgnosticStructs_1.HookType.Custom:
|
|
33
|
+
return ethers_1.ethers.AbiCoder.defaultAbiCoder().decode(['tuple(bool,address,uint256,bytes,bytes)'], hook.hookData)[0];
|
|
34
|
+
case AgnosticStructs_1.HookType.FullBalanceApprove:
|
|
35
|
+
return ethers_1.ethers.AbiCoder.defaultAbiCoder().decode(['tuple(address,address,bool)'], hook.hookData)[0];
|
|
36
|
+
case AgnosticStructs_1.HookType.FullBalanceTransfer:
|
|
37
|
+
return ethers_1.ethers.AbiCoder.defaultAbiCoder().decode(['tuple(address,address,bool)'], hook.hookData)[0];
|
|
38
|
+
default:
|
|
39
|
+
throw new Error(`Unknown hook type: ${hook.hookType}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
throw new Error(`Failed to decode hook data: ${error}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Debug helper: Get estimated gas for a ZapCall
|
|
48
|
+
* @param zapCall - The zap call to estimate the gas usage for
|
|
49
|
+
* @returns The estimated gas usage
|
|
50
|
+
*/
|
|
51
|
+
estimateGasUsage(zapCall) {
|
|
52
|
+
// Rough estimation based on hook types and operations
|
|
53
|
+
let gasEstimate = 50000; // Base gas
|
|
54
|
+
zapCall.hooks.forEach((hook) => {
|
|
55
|
+
switch (hook.hookType) {
|
|
56
|
+
case AgnosticStructs_1.HookType.Custom:
|
|
57
|
+
gasEstimate += 100000; // Custom calls can vary widely
|
|
58
|
+
break;
|
|
59
|
+
case AgnosticStructs_1.HookType.FullBalanceApprove:
|
|
60
|
+
gasEstimate += 50000; // ERC20 approve
|
|
61
|
+
break;
|
|
62
|
+
case AgnosticStructs_1.HookType.FullBalanceTransfer:
|
|
63
|
+
gasEstimate += 65000; // ERC20 transfer
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
if (zapCall.bridgeData.isRequired) {
|
|
68
|
+
gasEstimate += 200000; // Bridge operations
|
|
69
|
+
}
|
|
70
|
+
return gasEstimate;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Visualize ZapCall chain - Human readable description of all operations
|
|
74
|
+
* @param zapCall - The zap call to visualize
|
|
75
|
+
*/
|
|
76
|
+
visualizeZapCall(zapCall, contractInterfaces) {
|
|
77
|
+
console.log('🔗 ZapCall Chain Visualization');
|
|
78
|
+
console.log('================================');
|
|
79
|
+
if (zapCall.hooks.length === 0) {
|
|
80
|
+
console.log('❌ No hooks in this ZapCall');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
zapCall.hooks.forEach((hook, index) => {
|
|
84
|
+
const stepNumber = (index + 1).toString().padStart(2, ' ');
|
|
85
|
+
console.log(`\n${stepNumber}. ${this._describeHook(hook, contractInterfaces)}`);
|
|
86
|
+
});
|
|
87
|
+
// Bridge information
|
|
88
|
+
if (zapCall.bridgeData.isRequired) {
|
|
89
|
+
console.log('\n🌉 Bridge Operations:');
|
|
90
|
+
if (zapCall.bridgeData.tokens.length > 0) {
|
|
91
|
+
console.log(` 📤 Bridge tokens: ${zapCall.bridgeData.tokens.map((t) => this._formatAddress(t)).join(', ')}`);
|
|
92
|
+
}
|
|
93
|
+
if (zapCall.bridgeData.nfts.length > 0) {
|
|
94
|
+
console.log(` 🖼️ Bridge NFTs: ${zapCall.bridgeData.nfts.length} NFT(s)`);
|
|
95
|
+
zapCall.bridgeData.nfts.forEach((nft, i) => {
|
|
96
|
+
console.log(` ${i + 1}. ${this._formatAddress(nft.nft)} #${nft.id} (amount: ${nft.amount})`);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
console.log('\n🚫 No bridge operations required');
|
|
102
|
+
}
|
|
103
|
+
// Summary
|
|
104
|
+
console.log('\n📊 Summary:');
|
|
105
|
+
console.log(` Total hooks: ${zapCall.hooks.length}`);
|
|
106
|
+
console.log(` Estimated gas: ${this.estimateGasUsage(zapCall).toLocaleString()}`);
|
|
107
|
+
console.log(` Bridge required: ${zapCall.bridgeData.isRequired ? 'Yes' : 'No'}`);
|
|
108
|
+
console.log('================================');
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Private helper to describe individual hooks
|
|
112
|
+
* @param hook - The hook to describe
|
|
113
|
+
* @returns The description of the hook
|
|
114
|
+
*/
|
|
115
|
+
_describeHook(hook, contractInterfaces) {
|
|
116
|
+
try {
|
|
117
|
+
switch (hook.hookType) {
|
|
118
|
+
case AgnosticStructs_1.HookType.Custom:
|
|
119
|
+
return this._describeCustomHook(hook, contractInterfaces);
|
|
120
|
+
case AgnosticStructs_1.HookType.FullBalanceApprove:
|
|
121
|
+
return this._describeApproveHook(hook);
|
|
122
|
+
case AgnosticStructs_1.HookType.FullBalanceTransfer:
|
|
123
|
+
return this._describeTransferHook(hook);
|
|
124
|
+
default:
|
|
125
|
+
return `❓ Unknown hook type: ${hook.hookType}`;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
return `❌ Error describing hook: ${error}`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Describe custom hook with function details
|
|
134
|
+
* @param hook - The hook to describe
|
|
135
|
+
* @returns The description of the hook
|
|
136
|
+
*/
|
|
137
|
+
_describeCustomHook(hook, contractInterfaces) {
|
|
138
|
+
const decoded = this.decodeHookData(hook);
|
|
139
|
+
const [isFromSA, contractAddress, value, data, improvedMissionInfo] = decoded;
|
|
140
|
+
// Try to decode function name from data
|
|
141
|
+
let functionDescription = 'unknown function';
|
|
142
|
+
let hasReplacements = false;
|
|
143
|
+
if (data && data.length >= 10) {
|
|
144
|
+
// At least 4 bytes for selector + some data
|
|
145
|
+
const selector = data.slice(0, 10); // "0x" + 8 hex chars
|
|
146
|
+
// Try to find function name from registered interfaces
|
|
147
|
+
for (const [address, contractInterface] of contractInterfaces) {
|
|
148
|
+
if (address === contractAddress.toLowerCase()) {
|
|
149
|
+
try {
|
|
150
|
+
const fragment = contractInterface.getFunction(selector);
|
|
151
|
+
if (fragment) {
|
|
152
|
+
functionDescription = `${fragment.name}(${fragment.inputs.map((input) => input.type).join(', ')})`;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// Function not found in this interface
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// If not found in registered interfaces, just show selector
|
|
162
|
+
if (functionDescription === 'unknown function') {
|
|
163
|
+
functionDescription = `function with selector ${selector}`;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Check for dynamic replacements
|
|
167
|
+
if (improvedMissionInfo && improvedMissionInfo !== '0x' && improvedMissionInfo.length > 2) {
|
|
168
|
+
hasReplacements = true;
|
|
169
|
+
}
|
|
170
|
+
const perspective = isFromSA ? 'Smart Account' : 'Proxy Contract';
|
|
171
|
+
const valueStr = value > 0n ? ` (sending ${ethers_1.ethers.formatEther(value)} ETH)` : '';
|
|
172
|
+
const replacementStr = hasReplacements ? ' 🔄 [with dynamic value replacement]' : '';
|
|
173
|
+
return `📞 Custom call to ${this._formatAddress(contractAddress)} from ${perspective}${valueStr}
|
|
174
|
+
Function: ${functionDescription}${replacementStr}`;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Describe approve hook
|
|
178
|
+
* @param hook - The hook to describe
|
|
179
|
+
* @returns The description of the hook
|
|
180
|
+
*/
|
|
181
|
+
_describeApproveHook(hook) {
|
|
182
|
+
const decoded = this.decodeHookData(hook);
|
|
183
|
+
const [token, to, isFromSA] = decoded;
|
|
184
|
+
const perspective = isFromSA ? 'Smart Account' : 'Proxy Contract';
|
|
185
|
+
return `✅ Approve full balance of ${this._formatAddress(token)} to ${this._formatAddress(to)} from ${perspective}`;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Describe transfer hook
|
|
189
|
+
* @param hook - The hook to describe
|
|
190
|
+
* @returns The description of the hook
|
|
191
|
+
*/
|
|
192
|
+
_describeTransferHook(hook) {
|
|
193
|
+
const decoded = this.decodeHookData(hook);
|
|
194
|
+
const [token, to, isFromSA] = decoded;
|
|
195
|
+
const perspective = isFromSA ? 'Smart Account' : 'Proxy Contract';
|
|
196
|
+
return `💸 Transfer full balance of ${this._formatAddress(token)} to ${this._formatAddress(to)} from ${perspective}`;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Format address for display (show first 6 and last 4 characters)
|
|
200
|
+
* @param address - The address to format
|
|
201
|
+
* @returns The formatted address
|
|
202
|
+
*/
|
|
203
|
+
_formatAddress(address) {
|
|
204
|
+
if (!address || address.length < 10)
|
|
205
|
+
return address;
|
|
206
|
+
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Get a detailed breakdown of a ZapCall for logging
|
|
210
|
+
* @param zapCall - The zap call to get the breakdown for
|
|
211
|
+
* @returns The breakdown of the zap call
|
|
212
|
+
*/
|
|
213
|
+
getZapCallBreakdown(zapCall, contractInterfaces) {
|
|
214
|
+
const hookTypes = {};
|
|
215
|
+
const hookDescriptions = [];
|
|
216
|
+
zapCall.hooks.forEach((hook, index) => {
|
|
217
|
+
const typeName = AgnosticStructs_1.HookType[hook.hookType];
|
|
218
|
+
hookTypes[typeName] = (hookTypes[typeName] || 0) + 1;
|
|
219
|
+
hookDescriptions.push(`${index + 1}. ${this._describeHook(hook, contractInterfaces)}`);
|
|
220
|
+
});
|
|
221
|
+
return {
|
|
222
|
+
totalHooks: zapCall.hooks.length,
|
|
223
|
+
hookTypes,
|
|
224
|
+
gasEstimate: this.estimateGasUsage(zapCall),
|
|
225
|
+
encodedSize: this.encodeZapCall(zapCall).length / 2, // bytes
|
|
226
|
+
bridgeRequired: zapCall.bridgeData.isRequired,
|
|
227
|
+
hookDescriptions,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Compare two ZapCalls and show differences
|
|
232
|
+
* @param zapCall1 - The first zap call to compare
|
|
233
|
+
* @param zapCall2 - The second zap call to compare
|
|
234
|
+
* @param label1 - The label of the first zap call
|
|
235
|
+
* @param label2 - The label of the second zap call
|
|
236
|
+
*/
|
|
237
|
+
compareZapCalls(zapCall1, zapCall2, label1 = 'ZapCall 1', label2 = 'ZapCall 2', contractInterfaces) {
|
|
238
|
+
console.log(`🔄 Comparing ${label1} vs ${label2}`);
|
|
239
|
+
console.log('='.repeat(50));
|
|
240
|
+
const breakdown1 = this.getZapCallBreakdown(zapCall1, contractInterfaces);
|
|
241
|
+
const breakdown2 = this.getZapCallBreakdown(zapCall2, contractInterfaces);
|
|
242
|
+
console.log(`📊 ${label1}:`);
|
|
243
|
+
console.log(` Hooks: ${breakdown1.totalHooks}, Gas: ${breakdown1.gasEstimate.toLocaleString()}, Size: ${breakdown1.encodedSize} bytes`);
|
|
244
|
+
console.log(`📊 ${label2}:`);
|
|
245
|
+
console.log(` Hooks: ${breakdown2.totalHooks}, Gas: ${breakdown2.gasEstimate.toLocaleString()}, Size: ${breakdown2.encodedSize} bytes`);
|
|
246
|
+
console.log('\n📈 Differences:');
|
|
247
|
+
console.log(` Hooks: ${breakdown2.totalHooks - breakdown1.totalHooks > 0 ? '+' : ''}${breakdown2.totalHooks - breakdown1.totalHooks}`);
|
|
248
|
+
console.log(` Gas: ${breakdown2.gasEstimate - breakdown1.gasEstimate > 0 ? '+' : ''}${(breakdown2.gasEstimate - breakdown1.gasEstimate).toLocaleString()}`);
|
|
249
|
+
console.log(` Size: ${breakdown2.encodedSize - breakdown1.encodedSize > 0 ? '+' : ''}${breakdown2.encodedSize - breakdown1.encodedSize} bytes`);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Encode ZapCall for transaction
|
|
253
|
+
* @param zapCall - The zap call to encode
|
|
254
|
+
* @returns The encoded zap call that can be used as calldata in tac sdk
|
|
255
|
+
*/
|
|
256
|
+
encodeZapCall(zapCall) {
|
|
257
|
+
return ethers_1.ethers.AbiCoder.defaultAbiCoder().encode([
|
|
258
|
+
'tuple(' +
|
|
259
|
+
'tuple(uint8,bytes)[],' + // hooks: only hookType and hookData
|
|
260
|
+
'tuple(address[],tuple(address,uint256,uint256)[],bool)' + // bridgeData
|
|
261
|
+
')',
|
|
262
|
+
], [
|
|
263
|
+
[
|
|
264
|
+
zapCall.hooks.map((hook) => [hook.hookType, hook.hookData]),
|
|
265
|
+
[
|
|
266
|
+
zapCall.bridgeData.tokens,
|
|
267
|
+
zapCall.bridgeData.nfts.map((nft) => [nft.nft, nft.id, nft.amount]),
|
|
268
|
+
zapCall.bridgeData.isRequired,
|
|
269
|
+
],
|
|
270
|
+
],
|
|
271
|
+
]);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
exports.DebugHelpers = DebugHelpers;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Interface } from 'ethers';
|
|
2
|
+
import { AmountChange, Hook } from './AgnosticStructs';
|
|
3
|
+
export declare class HooksHandler {
|
|
4
|
+
/**
|
|
5
|
+
* Create a custom hook with optional dynamic value replacement
|
|
6
|
+
* @param contractAddress - The address of the contract to call
|
|
7
|
+
* @param functionName - The name of the function to call
|
|
8
|
+
* @param params - The parameters of the function to call
|
|
9
|
+
* @param options - The options of the custom hook
|
|
10
|
+
* @returns The custom hook
|
|
11
|
+
*/
|
|
12
|
+
createCustomHook(contractAddress: string, functionName: string, params: any[], contractInterfaces: Map<string, Interface>, options?: {
|
|
13
|
+
isFromSAPerspective?: boolean;
|
|
14
|
+
value?: bigint;
|
|
15
|
+
dynamicReplacements?: AmountChange[];
|
|
16
|
+
}): Hook;
|
|
17
|
+
/**
|
|
18
|
+
* Create a full balance approve hook
|
|
19
|
+
* @param token - The token to approve
|
|
20
|
+
* @param to - The address to approve to
|
|
21
|
+
* @param isFromSAPerspective - Whether the hook is from the smart account perspective or from proxy perspective
|
|
22
|
+
* @returns The full balance approve hook
|
|
23
|
+
*/
|
|
24
|
+
createFullBalanceApproveHook(token: string, to: string, isFromSAPerspective?: boolean): Hook;
|
|
25
|
+
/**
|
|
26
|
+
* Create a full balance transfer hook
|
|
27
|
+
* @param token - The token to transfer
|
|
28
|
+
* @param to - The address to transfer to
|
|
29
|
+
* @param isFromSAPerspective - Whether the hook is from the smart account perspective or from proxy perspective
|
|
30
|
+
* @returns The full balance transfer hook
|
|
31
|
+
*/
|
|
32
|
+
createFullBalanceTransferHook(token: string, to: string, isFromSAPerspective?: boolean): Hook;
|
|
33
|
+
/**
|
|
34
|
+
* Utility: Create multiple approve hooks at once
|
|
35
|
+
* @param approvals - The approvals to create
|
|
36
|
+
* @returns The multiple approve hooks
|
|
37
|
+
*/
|
|
38
|
+
createMultipleApproves(approvals: {
|
|
39
|
+
token: string;
|
|
40
|
+
spender: string;
|
|
41
|
+
isFromSA?: boolean;
|
|
42
|
+
}[]): Hook[];
|
|
43
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HooksHandler = void 0;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
const AgnosticStructs_1 = require("./AgnosticStructs");
|
|
6
|
+
class HooksHandler {
|
|
7
|
+
/**
|
|
8
|
+
* Create a custom hook with optional dynamic value replacement
|
|
9
|
+
* @param contractAddress - The address of the contract to call
|
|
10
|
+
* @param functionName - The name of the function to call
|
|
11
|
+
* @param params - The parameters of the function to call
|
|
12
|
+
* @param options - The options of the custom hook
|
|
13
|
+
* @returns The custom hook
|
|
14
|
+
*/
|
|
15
|
+
createCustomHook(contractAddress, functionName, params, contractInterfaces, options = {}) {
|
|
16
|
+
const { isFromSAPerspective = true, value = 0n, dynamicReplacements } = options;
|
|
17
|
+
const contractInterface = contractInterfaces.get(contractAddress.toLowerCase());
|
|
18
|
+
if (!contractInterface) {
|
|
19
|
+
throw new Error(`Contract interface not found for address: ${contractAddress}`);
|
|
20
|
+
}
|
|
21
|
+
const data = contractInterface.encodeFunctionData(functionName, params);
|
|
22
|
+
let improvedMissionInfo = '0x';
|
|
23
|
+
// If dynamic replacements are specified, encode them
|
|
24
|
+
if (dynamicReplacements && dynamicReplacements.length > 0) {
|
|
25
|
+
// For now, support single replacement (can be extended)
|
|
26
|
+
const replacement = dynamicReplacements[0];
|
|
27
|
+
const replacementData = ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(['tuple(uint16,uint16,address,address)'], [[replacement.position, replacement.len, replacement.token, replacement.balanceAddress]]);
|
|
28
|
+
// Encode as ReplacementType.Amount with the replacement data
|
|
29
|
+
improvedMissionInfo = ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(['uint8', 'bytes'], [AgnosticStructs_1.ReplacementType.Amount, replacementData]);
|
|
30
|
+
}
|
|
31
|
+
const customHookData = {
|
|
32
|
+
isFromSAPerspective,
|
|
33
|
+
contractAddress,
|
|
34
|
+
value,
|
|
35
|
+
data,
|
|
36
|
+
improvedMissionInfo,
|
|
37
|
+
};
|
|
38
|
+
// Encode only the CustomHookData
|
|
39
|
+
const encodedHookData = ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(['tuple(bool,address,uint256,bytes,bytes)'], [
|
|
40
|
+
[
|
|
41
|
+
customHookData.isFromSAPerspective,
|
|
42
|
+
customHookData.contractAddress,
|
|
43
|
+
customHookData.value,
|
|
44
|
+
customHookData.data,
|
|
45
|
+
customHookData.improvedMissionInfo,
|
|
46
|
+
],
|
|
47
|
+
]);
|
|
48
|
+
return {
|
|
49
|
+
hookType: AgnosticStructs_1.HookType.Custom,
|
|
50
|
+
hookData: encodedHookData,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create a full balance approve hook
|
|
55
|
+
* @param token - The token to approve
|
|
56
|
+
* @param to - The address to approve to
|
|
57
|
+
* @param isFromSAPerspective - Whether the hook is from the smart account perspective or from proxy perspective
|
|
58
|
+
* @returns The full balance approve hook
|
|
59
|
+
*/
|
|
60
|
+
createFullBalanceApproveHook(token, to, isFromSAPerspective = true) {
|
|
61
|
+
const approveHookData = {
|
|
62
|
+
token,
|
|
63
|
+
to,
|
|
64
|
+
isFromSAPerspective,
|
|
65
|
+
};
|
|
66
|
+
// Encode only the ApproveHookData
|
|
67
|
+
const encodedHookData = ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(['tuple(address,address,bool)'], [[approveHookData.token, approveHookData.to, approveHookData.isFromSAPerspective]]);
|
|
68
|
+
return {
|
|
69
|
+
hookType: AgnosticStructs_1.HookType.FullBalanceApprove,
|
|
70
|
+
hookData: encodedHookData,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Create a full balance transfer hook
|
|
75
|
+
* @param token - The token to transfer
|
|
76
|
+
* @param to - The address to transfer to
|
|
77
|
+
* @param isFromSAPerspective - Whether the hook is from the smart account perspective or from proxy perspective
|
|
78
|
+
* @returns The full balance transfer hook
|
|
79
|
+
*/
|
|
80
|
+
createFullBalanceTransferHook(token, to, isFromSAPerspective = true) {
|
|
81
|
+
const transferHookData = {
|
|
82
|
+
token,
|
|
83
|
+
to,
|
|
84
|
+
isFromSAPerspective,
|
|
85
|
+
};
|
|
86
|
+
// Encode only the TransferHookData
|
|
87
|
+
const encodedHookData = ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(['tuple(address,address,bool)'], [[transferHookData.token, transferHookData.to, transferHookData.isFromSAPerspective]]);
|
|
88
|
+
return {
|
|
89
|
+
hookType: AgnosticStructs_1.HookType.FullBalanceTransfer,
|
|
90
|
+
hookData: encodedHookData,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Utility: Create multiple approve hooks at once
|
|
95
|
+
* @param approvals - The approvals to create
|
|
96
|
+
* @returns The multiple approve hooks
|
|
97
|
+
*/
|
|
98
|
+
createMultipleApproves(approvals) {
|
|
99
|
+
return approvals.map((approval) => this.createFullBalanceApproveHook(approval.token, approval.spender, approval.isFromSA ?? true));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.HooksHandler = HooksHandler;
|