-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdebug-airdrop-failure.js
More file actions
178 lines (152 loc) · 6.95 KB
/
debug-airdrop-failure.js
File metadata and controls
178 lines (152 loc) · 6.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
import { ethers } from 'ethers';
// Contract addresses from the error
const MYSBT_ADDRESS = '0xD1e6BDfb907EacD26FF69a40BBFF9278b1E7Cf5C';
const OPERATOR_ADDRESS = '0xF7Bf79AcB7F3702b9DbD397d8140ac9DE6Ce642C';
const TARGET_USER_1 = '0x935f8694855fa9f1d1520e43689219ed4fff8c97';
const TARGET_USER_2 = '0x65cc20b63739d372176f1501c253de4e03254d16';
// RPC
const provider = new ethers.JsonRpcProvider('https://rpc.ankr.com/eth_sepolia');
// Get addresses from shared-config
const CORE_ADDRESSES = {
gToken: '0x28Cb0B324cb9b9fA3Bf84d1E0dA1AC6603113A2d',
registry: '0xAc1D1452e19492eFbb8b331c0B71cb634b09f5a9'
};
const MySBTABI = [
'function balanceOf(address owner) view returns (uint256)',
'function paused() view returns (bool)',
'function airdropMint(address user, string memory metadata) returns (uint256, bool)',
'function hasRole(bytes32 role, address account) view returns (bool)',
'function MINTER_ROLE() view returns (bytes32)'
];
const GTokenABI = [
'function balanceOf(address) view returns (uint256)',
'function allowance(address owner, address spender) view returns (uint256)',
'function decimals() view returns (uint8)'
];
const RegistryABI = [
'function isRegisteredCommunity(address) view returns (bool)',
'function getCommunityProfile(address) view returns (tuple(string name, uint8 nodeType, uint256 registeredAt, bool isActive))'
];
async function main() {
console.log('🔍 Debugging Airdrop Failure\n');
console.log('Operator:', OPERATOR_ADDRESS);
console.log('MySBT Contract:', MYSBT_ADDRESS);
console.log('Target Users:', [TARGET_USER_1, TARGET_USER_2]);
console.log('');
const mySBT = new ethers.Contract(MYSBT_ADDRESS, MySBTABI, provider);
const gToken = new ethers.Contract(CORE_ADDRESSES.gToken, GTokenABI, provider);
const registry = new ethers.Contract(CORE_ADDRESSES.registry, RegistryABI, provider);
try {
// 1. Check if contract is paused
console.log('1️⃣ Checking MySBT contract status...');
try {
const isPaused = await mySBT.paused();
console.log(` Contract paused: ${isPaused}`);
if (isPaused) {
console.log(' ❌ CONTRACT IS PAUSED - This is why airdrop fails!');
return;
}
} catch (e) {
console.log(' ⚠️ Could not check paused status (function may not exist)');
}
// 2. Check if operator is registered
console.log('\n2️⃣ Checking operator registration...');
const isRegistered = await registry.isRegisteredCommunity(OPERATOR_ADDRESS);
console.log(` Operator registered: ${isRegistered}`);
if (!isRegistered) {
console.log(' ❌ OPERATOR NOT REGISTERED - This is why airdrop fails!');
return;
}
const profile = await registry.getCommunityProfile(OPERATOR_ADDRESS);
console.log(` Community name: ${profile.name}`);
console.log(` Node type: ${profile.nodeType}`);
console.log(` Is active: ${profile.isActive}`);
if (!profile.isActive) {
console.log(' ❌ COMMUNITY NOT ACTIVE - This is why airdrop fails!');
return;
}
// 3. Check GToken balance
console.log('\n3️⃣ Checking operator GToken balance...');
const decimals = await gToken.decimals();
const balance = await gToken.balanceOf(OPERATOR_ADDRESS);
const balanceFormatted = ethers.formatUnits(balance, decimals);
console.log(` Operator GToken balance: ${balanceFormatted} GT`);
const required = 0.4 * 2; // 2 addresses
console.log(` Required for 2 airdrops: ${required} GT`);
if (Number(balanceFormatted) < required) {
console.log(` ❌ INSUFFICIENT GTOKEN BALANCE - This is why airdrop fails!`);
return;
}
console.log(' ✅ Balance sufficient');
// 4. Check GToken allowance to MySBT
console.log('\n4️⃣ Checking GToken allowance...');
const allowance = await gToken.allowance(OPERATOR_ADDRESS, MYSBT_ADDRESS);
const allowanceFormatted = ethers.formatUnits(allowance, decimals);
console.log(` Current allowance: ${allowanceFormatted} GT`);
console.log(` Required allowance: ${required} GT`);
if (Number(allowanceFormatted) < required) {
console.log(` ❌ INSUFFICIENT ALLOWANCE - This is why airdrop fails!`);
console.log(` \n Solution: Operator needs to approve MySBT contract:`);
console.log(` gToken.approve("${MYSBT_ADDRESS}", ethers.parseEther("${required}"))`);
return;
}
console.log(' ✅ Allowance sufficient');
// 5. Check if users already have SBT
console.log('\n5️⃣ Checking if target users already have SBT...');
for (const user of [TARGET_USER_1, TARGET_USER_2]) {
const balance = await mySBT.balanceOf(user);
console.log(` ${user}: ${balance.toString()} SBTs`);
if (balance > 0n) {
console.log(` ⚠️ User already has SBT (will add membership, not mint new)`);
}
}
// 6. Check if MySBT contract has airdropMint function
console.log('\n6️⃣ Checking if airdropMint function exists...');
try {
// Try to call the function with callStatic to see if it exists
const metadata = JSON.stringify({
communityAddress: OPERATOR_ADDRESS,
communityName: "Test",
nodeType: "PAYMASTER_SUPER",
isActive: true
});
// This will fail but give us the actual revert reason
const tx = await mySBT.airdropMint.staticCall(TARGET_USER_1, metadata);
console.log(' ✅ Function exists and can be called (unexpected - should have succeeded)');
} catch (error) {
console.log(' ❌ Function call failed with error:');
console.log(' ', error.message);
// Try to extract revert reason
if (error.data) {
console.log(' Revert data:', error.data);
}
if (error.reason) {
console.log(' Revert reason:', error.reason);
}
// Check common issues
if (error.message.includes('function selector was not recognized')) {
console.log('\n ⚠️ AIRDROP_MINT FUNCTION DOES NOT EXIST IN CONTRACT');
console.log(' This contract may not be MySBT_v2.3.2 or later!');
}
}
console.log('\n7️⃣ Checking contract code at address...');
const code = await provider.getCode(MYSBT_ADDRESS);
console.log(` Contract code size: ${code.length} bytes`);
if (code === '0x') {
console.log(' ❌ NO CONTRACT AT THIS ADDRESS!');
} else {
// Check if airdropMint selector is in bytecode
const airdropMintSelector = '0x6a6385c0'; // First 4 bytes of keccak256("airdropMint(address,string)")
if (code.includes(airdropMintSelector.slice(2))) {
console.log(` ✅ airdropMint function selector found in bytecode`);
} else {
console.log(` ❌ airdropMint function selector NOT found in bytecode`);
console.log(` This contract does NOT have airdropMint function!`);
console.log(` You need to deploy MySBT_v2.3.2 or later.`);
}
}
} catch (error) {
console.error('\n❌ Error during checks:', error.message);
}
}
main().catch(console.error);