@sparkdotfi/abi-cli 0.2.0-20251031.00360a84 → 0.2.0-20260317.e49bd3f1
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/src/abi-registry/abi-fetcher/AbiFetcherClient.js +15 -4
- package/dist/src/abi-registry/getAbiForContract.js +41 -2
- package/dist/src/manifest/manifest.js +3 -4
- package/dist/types/src/manifest/manifest.d.ts +4 -7
- package/dist/types/src/manifest/manifest.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -26,12 +26,22 @@ export class AbiFetcherClient {
|
|
|
26
26
|
return result.abi;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
function createAbiLoader(domain, etherscanApiKey) {
|
|
30
|
+
// Etherscan v2 API's contract/getabi module is broken for Gnosis (returns "Unknown Exception"),
|
|
31
|
+
// so we use Blockscout directly for Gnosis.
|
|
32
|
+
if (domain === 'gnosis') {
|
|
33
|
+
return new whatsabi.loaders.BlockscoutABILoader({
|
|
34
|
+
baseURL: 'https://gnosis.blockscout.com/api',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return new whatsabi.loaders.EtherscanV2ABILoader({
|
|
32
38
|
apiKey: etherscanApiKey,
|
|
33
39
|
chainId: domainToChainId[domain],
|
|
34
40
|
});
|
|
41
|
+
}
|
|
42
|
+
async function loadAbiUsingWhatsAbi(args) {
|
|
43
|
+
const { address, domain, client, etherscanApiKey } = args;
|
|
44
|
+
const loader = createAbiLoader(domain, etherscanApiKey);
|
|
35
45
|
const data = await lazyPipe(() => whatsabi.autoload(address, {
|
|
36
46
|
provider: client,
|
|
37
47
|
followProxies: true,
|
|
@@ -44,7 +54,8 @@ async function loadAbiUsingWhatsAbi(args) {
|
|
|
44
54
|
if (data.abi.length === 0) {
|
|
45
55
|
throw new Error(`Abi not found for ${domain}:${address}`);
|
|
46
56
|
}
|
|
47
|
-
|
|
57
|
+
const loaderName = data.abiLoadedFrom?.name.toLowerCase() ?? '';
|
|
58
|
+
if (!data.abiLoadedFrom || !(loaderName.includes('etherscan') || loaderName.includes('blockscout'))) {
|
|
48
59
|
throw new Error(`Abi not found for ${domain}:${address}`);
|
|
49
60
|
}
|
|
50
61
|
return data;
|
|
@@ -12,10 +12,49 @@ export function getAbiForContract(abiRegistry, config, name) {
|
|
|
12
12
|
}));
|
|
13
13
|
assert(instances.length > 0);
|
|
14
14
|
const rootAbi = instances[0].abi;
|
|
15
|
-
const
|
|
15
|
+
const rootNormalized = normalizeAbiForComparison(rootAbi);
|
|
16
16
|
const notMatchingAbi = instances.find(({ abi }) => {
|
|
17
|
-
return
|
|
17
|
+
return normalizeAbiForComparison(abi) !== rootNormalized;
|
|
18
18
|
});
|
|
19
19
|
assert(!notMatchingAbi, `Found not matching abis: ${notMatchingAbi?.name}`);
|
|
20
20
|
return rootAbi;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Normalizes an ABI for comparison by removing source-specific differences:
|
|
24
|
+
* - Strips `internalType` fields (compiler metadata, not part of ABI spec)
|
|
25
|
+
* - Sorts entries by type + name for consistent ordering
|
|
26
|
+
* Different ABI sources (Etherscan, Blockscout) may return the same ABI with cosmetic differences.
|
|
27
|
+
*/
|
|
28
|
+
function normalizeAbiForComparison(abi) {
|
|
29
|
+
const normalized = abi.map((entry) => {
|
|
30
|
+
const clone = structuredClone(entry);
|
|
31
|
+
// Constructors and fallback/receive without stateMutability are implicitly "nonpayable"
|
|
32
|
+
if ((clone.type === 'constructor' || clone.type === 'fallback' || clone.type === 'receive') &&
|
|
33
|
+
!clone.stateMutability) {
|
|
34
|
+
clone.stateMutability = 'nonpayable';
|
|
35
|
+
}
|
|
36
|
+
return normalizeAbiEntry(clone);
|
|
37
|
+
});
|
|
38
|
+
normalized.sort((a, b) => {
|
|
39
|
+
const typeCmp = (a.type ?? '').localeCompare(b.type ?? '');
|
|
40
|
+
if (typeCmp !== 0)
|
|
41
|
+
return typeCmp;
|
|
42
|
+
return (a.name ?? '').localeCompare(b.name ?? '');
|
|
43
|
+
});
|
|
44
|
+
return JSON.stringify(normalized);
|
|
45
|
+
}
|
|
46
|
+
function normalizeAbiEntry(obj) {
|
|
47
|
+
if (Array.isArray(obj)) {
|
|
48
|
+
return obj.map(normalizeAbiEntry);
|
|
49
|
+
}
|
|
50
|
+
if (obj !== null && typeof obj === 'object') {
|
|
51
|
+
const result = {};
|
|
52
|
+
for (const key of Object.keys(obj).sort()) {
|
|
53
|
+
if (key === 'internalType')
|
|
54
|
+
continue;
|
|
55
|
+
result[key] = normalizeAbiEntry(obj[key]);
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
return obj;
|
|
60
|
+
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { checkedAddressSchema, getUnsafeChainIdToDomain, } from '@sparkdotfi/common-universal';
|
|
2
2
|
import { entries, mapKeys } from 'remeda';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
export const CheckedAddressSchema = z.string().transform(CheckedAddress);
|
|
5
4
|
export const FullAddressSchema = z.object({
|
|
6
|
-
address:
|
|
5
|
+
address: checkedAddressSchema,
|
|
7
6
|
skipAbiVerification: z.boolean().default(false),
|
|
8
7
|
});
|
|
9
8
|
export const NakedOrFullAddressSchema = z
|
|
10
|
-
.union([
|
|
9
|
+
.union([checkedAddressSchema, FullAddressSchema])
|
|
11
10
|
.transform((union) => {
|
|
12
11
|
if (typeof union === 'string') {
|
|
13
12
|
return { address: union, skipAbiVerification: false };
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { CheckedAddress, SparkDomain } from '@sparkdotfi/common-universal';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
export declare const CheckedAddressSchema: z.ZodEffects<z.ZodString, `0x${string}` & {
|
|
4
|
-
readonly __TAG__: "CheckedAddress";
|
|
5
|
-
}, string>;
|
|
6
3
|
export declare const FullAddressSchema: z.ZodObject<{
|
|
7
4
|
address: z.ZodEffects<z.ZodString, `0x${string}` & {
|
|
8
5
|
readonly __TAG__: "CheckedAddress";
|
|
@@ -873,10 +870,6 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
873
870
|
};
|
|
874
871
|
useCheckedAddress?: boolean | undefined;
|
|
875
872
|
};
|
|
876
|
-
eoa?: Record<string, Record<string, string | {
|
|
877
|
-
address: string;
|
|
878
|
-
skipAbiVerification?: boolean | undefined;
|
|
879
|
-
}>> | undefined;
|
|
880
873
|
contracts?: Record<string, Record<string, string | {
|
|
881
874
|
address: string;
|
|
882
875
|
skipAbiVerification?: boolean | undefined;
|
|
@@ -884,6 +877,10 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
884
877
|
address: string;
|
|
885
878
|
skipAbiVerification?: boolean | undefined;
|
|
886
879
|
}>>> | undefined;
|
|
880
|
+
eoa?: Record<string, Record<string, string | {
|
|
881
|
+
address: string;
|
|
882
|
+
skipAbiVerification?: boolean | undefined;
|
|
883
|
+
}>> | undefined;
|
|
887
884
|
interfaces?: Record<string, {
|
|
888
885
|
addresses: Record<string, string | {
|
|
889
886
|
address: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../../src/manifest/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../../src/manifest/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EAGd,WAAW,EACZ,MAAM,8BAA8B,CAAA;AAErC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;EAG5B,CAAA;AACF,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;EAQjC,CAAA;AAEJ,eAAO,MAAM,qCAAqC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAO9C,CAAA;AAEJ,eAAO,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAK5C,CAAA;AAEH,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAKxC,CAAA;AAEH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAuD,CAAA;AAE9E,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAA2D,CAAA;AAEvF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAIpC,CAAA;AACF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAAkD,CAAA;AAE/E,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYzB,CAAA;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA;AACxD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,cAAc,CAAC,CAAA;AACtD,MAAM,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAA;AACvC,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAC5E,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kCAAkC,CAAC,CAAA;AAC/F,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAA;AACzE,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qCAAqC,CAAC,CAAA;AAE5F,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sparkdotfi/abi-cli",
|
|
3
|
-
"version": "0.2.0-
|
|
3
|
+
"version": "0.2.0-20260317.e49bd3f1",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=22.0.0"
|
|
6
6
|
},
|
|
@@ -45,6 +45,6 @@
|
|
|
45
45
|
"prettier": "^3.6.2",
|
|
46
46
|
"remeda": "^2.30.0",
|
|
47
47
|
"viem": "^2.0.0",
|
|
48
|
-
"zod": "^3.
|
|
48
|
+
"zod": "^3.24.0"
|
|
49
49
|
}
|
|
50
50
|
}
|