@midnames/sdk 1.0.0 → 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/dist/core.d.ts +2 -1
- package/dist/core.js +25 -17
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/managed/index.d.ts +3 -2
- package/dist/managed/index.js +1 -13
- package/dist/managed/index.js.map +1 -1
- package/dist/managed/leaf.compact +70 -29
- package/dist/managed/managed/leaf/contract/index.d.ts +78 -25
- package/dist/managed/managed/leaf/contract/index.js +1251 -515
- package/dist/managed/managed/leaf/contract/index.js.map +1 -1
- package/dist/managed/utils.d.ts +5 -0
- package/dist/managed/utils.js +19 -0
- package/dist/managed/utils.js.map +1 -0
- package/dist/operations.d.ts +10 -39
- package/dist/operations.js +68 -352
- package/dist/provider.d.ts +8 -1
- package/dist/provider.js +31 -6
- package/dist/react/DomainProfileWidget.js +5 -2
- package/dist/react/HolographicCard.js +5 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +1 -2
- package/dist/types.d.ts +5 -1
- package/dist/utils/domain.d.ts +5 -0
- package/dist/utils/domain.js +18 -0
- package/dist/utils/imageResolver.js +19 -19
- package/dist/utils/sanitize.d.ts +6 -0
- package/dist/utils/sanitize.js +28 -0
- package/managed/index.js +1 -13
- package/managed/index.js.map +1 -1
- package/managed/leaf.compact +70 -29
- package/managed/managed/leaf/contract/index.js +1251 -515
- package/managed/managed/leaf/contract/index.js.map +1 -1
- package/managed/utils.js +19 -0
- package/managed/utils.js.map +1 -0
- package/package.json +4 -3
package/dist/provider.js
CHANGED
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
import { setNetworkId, } from "@midnight-ntwrk/midnight-js-network-id";
|
|
2
2
|
import { indexerPublicDataProvider } from "@midnight-ntwrk/midnight-js-indexer-public-data-provider";
|
|
3
|
+
export const NETWORK_REGISTRY = {
|
|
4
|
+
preview: {
|
|
5
|
+
indexerUrl: "https://indexer.preview.midnight.network/api/v3/graphql",
|
|
6
|
+
indexerWsUrl: "wss://indexer.preview.midnight.network/api/v3/graphql/ws",
|
|
7
|
+
tldAddress: "daa9fc4dfbd42ac9227f8b4358928532da68337689ea3b21b351d607890d9192",
|
|
8
|
+
},
|
|
9
|
+
preprod: {
|
|
10
|
+
indexerUrl: "https://indexer.preprod.midnight.network/api/v3/graphql",
|
|
11
|
+
indexerWsUrl: "wss://indexer.preprod.midnight.network/api/v3/graphql/ws",
|
|
12
|
+
tldAddress: "b00a62ce58c108b86af4670e662fa240844e0206c54009a539c64155d62e6b6a",
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
export function getNetworkConfig(networkId) {
|
|
16
|
+
const config = NETWORK_REGISTRY[networkId];
|
|
17
|
+
if (!config) {
|
|
18
|
+
throw new Error(`Unknown network "${networkId}". Known networks: ${Object.keys(NETWORK_REGISTRY).join(", ")}. Use createDefaultProvider() with explicit URLs for custom networks.`);
|
|
19
|
+
}
|
|
20
|
+
return config;
|
|
21
|
+
}
|
|
3
22
|
let defaultProvider = null;
|
|
4
|
-
export function getDefaultProvider() {
|
|
23
|
+
export function getDefaultProvider(networkId = "preview") {
|
|
5
24
|
if (!defaultProvider) {
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
defaultProvider = indexerPublicDataProvider(indexerUrl, indexerWsUrl);
|
|
25
|
+
const net = getNetworkConfig(networkId);
|
|
26
|
+
setNetworkId(networkId);
|
|
27
|
+
defaultProvider = indexerPublicDataProvider(net.indexerUrl, net.indexerWsUrl);
|
|
10
28
|
}
|
|
11
29
|
return defaultProvider;
|
|
12
30
|
}
|
|
@@ -14,7 +32,14 @@ export function setDefaultProvider(provider) {
|
|
|
14
32
|
defaultProvider = provider;
|
|
15
33
|
}
|
|
16
34
|
export function createDefaultProvider(config = {}) {
|
|
17
|
-
|
|
35
|
+
var _a, _b, _c;
|
|
36
|
+
const networkId = (_a = config.networkId) !== null && _a !== void 0 ? _a : "preview";
|
|
37
|
+
const knownConfig = NETWORK_REGISTRY[networkId];
|
|
38
|
+
const indexerUrl = (_b = config.indexerUrl) !== null && _b !== void 0 ? _b : knownConfig === null || knownConfig === void 0 ? void 0 : knownConfig.indexerUrl;
|
|
39
|
+
const indexerWsUrl = (_c = config.indexerWsUrl) !== null && _c !== void 0 ? _c : knownConfig === null || knownConfig === void 0 ? void 0 : knownConfig.indexerWsUrl;
|
|
40
|
+
if (!indexerUrl || !indexerWsUrl) {
|
|
41
|
+
throw new Error(`No indexer URLs for network "${networkId}". Provide indexerUrl and indexerWsUrl explicitly.`);
|
|
42
|
+
}
|
|
18
43
|
setNetworkId(networkId);
|
|
19
44
|
return indexerPublicDataProvider(indexerUrl, indexerWsUrl);
|
|
20
45
|
}
|
|
@@ -5,6 +5,7 @@ import { deriveShieldedAddress } from '../utils/address.js';
|
|
|
5
5
|
import { Copy, Check } from 'lucide-react';
|
|
6
6
|
import { getDomainProfile } from '../core.js';
|
|
7
7
|
import { getDefaultProvider } from '../provider.js';
|
|
8
|
+
import { sanitizeUrl } from '../utils/sanitize.js';
|
|
8
9
|
function truncate(addr, head = 10, tail = 6) {
|
|
9
10
|
if (!addr)
|
|
10
11
|
return 'N/A';
|
|
@@ -91,7 +92,9 @@ onFieldClick, onCopySuccess, }) {
|
|
|
91
92
|
}, [publicDataProvider, fullDomain, onError]);
|
|
92
93
|
const fieldsMap = useMemo(() => {
|
|
93
94
|
const m = {};
|
|
94
|
-
(
|
|
95
|
+
if (data === null || data === void 0 ? void 0 : data.fields) {
|
|
96
|
+
data.fields.forEach((v, k) => (m[k.toLowerCase()] = v));
|
|
97
|
+
}
|
|
95
98
|
return m;
|
|
96
99
|
}, [data]);
|
|
97
100
|
const displayName = fieldsMap['name'] || fieldsMap['displayname'] || fieldsMap['handle'] || '';
|
|
@@ -162,5 +165,5 @@ onFieldClick, onCopySuccess, }) {
|
|
|
162
165
|
if (error && !loading) {
|
|
163
166
|
return (_jsx("div", { className: cardClasses, children: _jsxs("div", { className: "midnames-error", children: [_jsx("div", { className: "midnames-error-title", children: "Error loading domain" }), _jsx("div", { className: "midnames-error-message", children: error.message }), _jsxs("div", { className: "midnames-error-code", children: ["Code: ", error.code] })] }) }));
|
|
164
167
|
}
|
|
165
|
-
return (_jsxs("div", { className: cardClasses, children: [showBanner && bannerUrl && _jsx("img", { src: bannerUrl, alt: "", className: "midnames-banner" }), _jsxs("div", { className: "midnames-header", children: [showAvatar && (_jsx("div", { className: "midnames-avatar", children: avatarUrl ? (_jsx("img", { src: avatarUrl, alt: "", onError: (e) => (e.currentTarget.style.display = 'none') })) : (_jsx("span", { children: (_c = (_b = (_a = (displayName || (data === null || data === void 0 ? void 0 : data.fullDomain) || '?')[0]) === null || _a === void 0 ? void 0 : _a.toUpperCase) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : '?' })) })), _jsxs("div", { className: "midnames-ident", children: [_jsx("div", { className: "midnames-name", children: displayName || (data === null || data === void 0 ? void 0 : data.fullDomain) || fullDomain }), variant !== 'minimal' && _jsx("div", { className: "midnames-domain", children: (data === null || data === void 0 ? void 0 : data.fullDomain) || fullDomain })] }), showStatus && (_jsx("div", { className: `midnames-status midnames-status-${status}`, children: loading ? 'Checking…' : status === 'registered' ? 'Registered' : status === 'available' ? 'Available' : status === 'error' ? 'Error' : 'Unknown' }))] }), showBio && bio && _jsx("p", { className: "midnames-bio", children: bio }), showDomainInfo && fieldsToShow.length > 0 && (_jsx("div", { className: "midnames-grid", children: fieldsToShow.map((field) => (_jsxs("div", { className: "midnames-box", children: [_jsx("div", { className: "midnames-box-label", children: field.label }), _jsx(CopyableValue, { value: field.value, className: "midnames-box-value", showCopy: showCopyButtons, onCopySuccess: onCopySuccess, onFieldClick: onFieldClick, fieldName: field.fieldName })] }, field.key))) })), showSocialLinks && (website || twitter || github || location) && (_jsxs("div", { className: "midnames-links", children: [website && (_jsx("a", { className: "midnames-chip", href: website.startsWith('http') ? website : `https://${website}
|
|
168
|
+
return (_jsxs("div", { className: cardClasses, children: [showBanner && bannerUrl && _jsx("img", { src: sanitizeUrl(bannerUrl), alt: "", className: "midnames-banner" }), _jsxs("div", { className: "midnames-header", children: [showAvatar && (_jsx("div", { className: "midnames-avatar", children: avatarUrl ? (_jsx("img", { src: sanitizeUrl(avatarUrl), alt: "", onError: (e) => (e.currentTarget.style.display = 'none') })) : (_jsx("span", { children: (_c = (_b = (_a = (displayName || (data === null || data === void 0 ? void 0 : data.fullDomain) || '?')[0]) === null || _a === void 0 ? void 0 : _a.toUpperCase) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : '?' })) })), _jsxs("div", { className: "midnames-ident", children: [_jsx("div", { className: "midnames-name", children: displayName || (data === null || data === void 0 ? void 0 : data.fullDomain) || fullDomain }), variant !== 'minimal' && _jsx("div", { className: "midnames-domain", children: (data === null || data === void 0 ? void 0 : data.fullDomain) || fullDomain })] }), showStatus && (_jsx("div", { className: `midnames-status midnames-status-${status}`, children: loading ? 'Checking…' : status === 'registered' ? 'Registered' : status === 'available' ? 'Available' : status === 'error' ? 'Error' : 'Unknown' }))] }), showBio && bio && _jsx("p", { className: "midnames-bio", children: bio }), showDomainInfo && fieldsToShow.length > 0 && (_jsx("div", { className: "midnames-grid", children: fieldsToShow.map((field) => (_jsxs("div", { className: "midnames-box", children: [_jsx("div", { className: "midnames-box-label", children: field.label }), _jsx(CopyableValue, { value: field.value, className: "midnames-box-value", showCopy: showCopyButtons, onCopySuccess: onCopySuccess, onFieldClick: onFieldClick, fieldName: field.fieldName })] }, field.key))) })), showSocialLinks && (website || twitter || github || location) && (_jsxs("div", { className: "midnames-links", children: [website && (_jsx("a", { className: "midnames-chip", href: sanitizeUrl(website.startsWith('http') ? website : `https://${website}`), target: "_blank", rel: "noreferrer", children: website.replace(/^https?:\/\//, '') })), twitter && (_jsxs("a", { className: "midnames-chip", href: `https://x.com/${twitter}`, target: "_blank", rel: "noreferrer", children: ["@", twitter] })), github && (_jsx("a", { className: "midnames-chip", href: `https://github.com/${github}`, target: "_blank", rel: "noreferrer", children: github })), location && _jsx("span", { className: "midnames-chip", children: location })] })), renderActions && (_jsx("div", { className: "midnames-actions", children: renderActions(data) }))] }));
|
|
166
169
|
}
|
|
@@ -3,6 +3,7 @@ import React, { useEffect, useRef, useCallback, useMemo, useState } from 'react'
|
|
|
3
3
|
import { MidnamesError } from '../errors.js';
|
|
4
4
|
import { getDomainProfile } from '../core.js';
|
|
5
5
|
import { getDefaultProvider } from '../provider.js';
|
|
6
|
+
import { sanitizeUrl } from '../utils/sanitize.js';
|
|
6
7
|
const DEFAULT_BEHIND_GRADIENT = 'radial-gradient(farthest-side circle at var(--midnames-holo-pointer-x) var(--midnames-holo-pointer-y),hsla(266,100%,90%,var(--midnames-holo-card-opacity)) 4%,hsla(266,50%,80%,calc(var(--midnames-holo-card-opacity)*0.75)) 10%,hsla(266,25%,70%,calc(var(--midnames-holo-card-opacity)*0.5)) 50%,hsla(266,0%,60%,0) 100%),radial-gradient(35% 52% at 55% 20%,#00ffaac4 0%,#073aff00 100%),radial-gradient(100% 100% at 50% 50%,#00c1ffff 1%,#073aff00 76%),conic-gradient(from 124deg at 50% 50%,#c137ffff 0%,#07c6ffff 40%,#07c6ffff 60%,#c137ffff 100%)';
|
|
7
8
|
const DEFAULT_INNER_GRADIENT = 'linear-gradient(145deg,#60496e8c 0%,#71C4FF44 100%)';
|
|
8
9
|
const ANIMATION_CONFIG = {
|
|
@@ -61,7 +62,9 @@ const HolographicCardComponent = ({ domain, publicDataProvider, behindGradient,
|
|
|
61
62
|
// Extract fields from domain data
|
|
62
63
|
const fieldsMap = useMemo(() => {
|
|
63
64
|
const m = {};
|
|
64
|
-
(
|
|
65
|
+
if (data === null || data === void 0 ? void 0 : data.fields) {
|
|
66
|
+
data.fields.forEach((v, k) => (m[k.toLowerCase()] = v));
|
|
67
|
+
}
|
|
65
68
|
return m;
|
|
66
69
|
}, [data]);
|
|
67
70
|
const displayName = fieldsMap['name'] || fieldsMap['displayname'] || fieldsMap['handle'] || '';
|
|
@@ -186,7 +189,7 @@ const HolographicCardComponent = ({ domain, publicDataProvider, behindGradient,
|
|
|
186
189
|
'--midnames-holo-behind-gradient': showBehindGradient ? (behindGradient !== null && behindGradient !== void 0 ? behindGradient : DEFAULT_BEHIND_GRADIENT) : 'none',
|
|
187
190
|
'--midnames-holo-inner-gradient': innerGradient !== null && innerGradient !== void 0 ? innerGradient : DEFAULT_INNER_GRADIENT,
|
|
188
191
|
}), [showBehindGradient, behindGradient, innerGradient]);
|
|
189
|
-
return (_jsx("div", { ref: wrapRef, className: `midnames-holo-card-wrapper ${className}`.trim(), style: cardStyle, children: _jsx("section", { ref: cardRef, className: "midnames-holo-card", children: _jsxs("div", { className: "midnames-holo-inside", children: [_jsx("div", { className: "midnames-holo-shine" }), _jsx("div", { className: "midnames-holo-glare" }), _jsxs("div", { className: "midnames-holo-content midnames-holo-avatar-content", children: [loading && (_jsx("div", { className: "midnames-holo-loading", children: "Loading domain..." })), error && !loading && (_jsxs("div", { className: "midnames-holo-error", children: [_jsx("div", { children: "Failed to load domain" }), _jsx("div", { style: { fontSize: '12px', opacity: 0.8, marginTop: '8px' }, children: error.message })] })), avatarUrl && !loading && !error && (_jsx("img", { className: "midnames-holo-avatar", src: avatarUrl, alt: `${displayName || domain} avatar`, loading: "lazy", onError: (e) => { e.target.style.display = 'none'; } })), showUserInfo && !loading && !error && (displayName || handle || contactText) && (_jsxs("div", { className: "midnames-holo-user-info", children: [_jsxs("div", { className: "midnames-holo-user-details", children: [_jsx("div", { className: "midnames-holo-mini-avatar", children: _jsx("img", { src: avatarUrl
|
|
192
|
+
return (_jsx("div", { ref: wrapRef, className: `midnames-holo-card-wrapper ${className}`.trim(), style: cardStyle, children: _jsx("section", { ref: cardRef, className: "midnames-holo-card", children: _jsxs("div", { className: "midnames-holo-inside", children: [_jsx("div", { className: "midnames-holo-shine" }), _jsx("div", { className: "midnames-holo-glare" }), _jsxs("div", { className: "midnames-holo-content midnames-holo-avatar-content", children: [loading && (_jsx("div", { className: "midnames-holo-loading", children: "Loading domain..." })), error && !loading && (_jsxs("div", { className: "midnames-holo-error", children: [_jsx("div", { children: "Failed to load domain" }), _jsx("div", { style: { fontSize: '12px', opacity: 0.8, marginTop: '8px' }, children: error.message })] })), avatarUrl && !loading && !error && (_jsx("img", { className: "midnames-holo-avatar", src: sanitizeUrl(avatarUrl), alt: `${displayName || domain} avatar`, loading: "lazy", onError: (e) => { e.target.style.display = 'none'; } })), showUserInfo && !loading && !error && (displayName || handle || contactText) && (_jsxs("div", { className: "midnames-holo-user-info", children: [_jsxs("div", { className: "midnames-holo-user-details", children: [_jsx("div", { className: "midnames-holo-mini-avatar", children: _jsx("img", { src: sanitizeUrl(avatarUrl), alt: "mini", loading: "lazy", onError: (e) => { e.target.style.display = 'none'; } }) }), _jsxs("div", { className: "midnames-holo-user-text", children: [displayName && _jsx("div", { className: "midnames-holo-handle", children: displayName }), handle && _jsxs("div", { className: "midnames-holo-status", children: ["@", handle] }), _jsx("div", { className: "midnames-holo-status", children: domain })] })] }), _jsx("button", { className: "midnames-holo-contact-btn", onClick: onContactClick, type: "button", children: contactText })] }))] })] }) }) }));
|
|
190
193
|
};
|
|
191
194
|
const HolographicCard = React.memo(HolographicCardComponent);
|
|
192
195
|
export default HolographicCard;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { DomainProfileWidget } from './DomainProfileWidget.js';
|
|
2
|
+
export type { DomainProfileWidgetProps } from './DomainProfileWidget.js';
|
|
2
3
|
export { default as HolographicCard } from './HolographicCard.js';
|
|
3
|
-
export { CopyableValue } from './DomainProfileWidget.js';
|
package/dist/react/index.js
CHANGED
package/dist/types.d.ts
CHANGED
package/dist/utils/domain.d.ts
CHANGED
|
@@ -14,3 +14,8 @@ export declare function getParentDomain(fullDomain: string): string | null;
|
|
|
14
14
|
export declare function getSubdomain(fullDomain: string): string;
|
|
15
15
|
export declare function isTLD(domain: string, tld?: string): boolean;
|
|
16
16
|
export declare function buildFullDomain(subdomain: string, parent: string): string;
|
|
17
|
+
export declare function domainToKey(name: string): {
|
|
18
|
+
key: Uint8Array;
|
|
19
|
+
len: bigint;
|
|
20
|
+
};
|
|
21
|
+
export declare function keyToDomain(key: Uint8Array): string;
|
package/dist/utils/domain.js
CHANGED
|
@@ -55,3 +55,21 @@ export function buildFullDomain(subdomain, parent) {
|
|
|
55
55
|
return parent;
|
|
56
56
|
return `${subdomain}.${parent}`;
|
|
57
57
|
}
|
|
58
|
+
export function domainToKey(name) {
|
|
59
|
+
const bytes = new TextEncoder().encode(name);
|
|
60
|
+
if (bytes.length === 0 || bytes.length > 32)
|
|
61
|
+
throw new Error(`Domain name must be 1-32 bytes, got ${bytes.length}`);
|
|
62
|
+
const key = new Uint8Array(32).fill(255);
|
|
63
|
+
key.set(bytes);
|
|
64
|
+
return { key, len: BigInt(bytes.length) };
|
|
65
|
+
}
|
|
66
|
+
export function keyToDomain(key) {
|
|
67
|
+
let len = 32;
|
|
68
|
+
for (let i = 0; i < 32; i++) {
|
|
69
|
+
if (key[i] === 255) {
|
|
70
|
+
len = i;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return new TextDecoder().decode(key.subarray(0, len));
|
|
75
|
+
}
|
|
@@ -27,37 +27,37 @@ function isValidIPFSCid(cid) {
|
|
|
27
27
|
* @returns Promise resolving to gateway URL
|
|
28
28
|
*/
|
|
29
29
|
async function resolveIPFS(cid) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
let
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
const controllers = DEFAULT_IPFS_GATEWAYS.map(() => new AbortController());
|
|
31
|
+
// Race all gateways — first successful response wins
|
|
32
|
+
const racePromise = new Promise((resolve, reject) => {
|
|
33
|
+
let pending = DEFAULT_IPFS_GATEWAYS.length;
|
|
34
|
+
DEFAULT_IPFS_GATEWAYS.forEach(async (gateway, i) => {
|
|
35
|
+
const timeoutId = setTimeout(() => controllers[i].abort(), 3000);
|
|
36
36
|
try {
|
|
37
|
-
const controller = new AbortController();
|
|
38
|
-
const timeoutId = setTimeout(() => controller.abort(), 3000);
|
|
39
37
|
const res = await fetch(`${gateway}/${cid}`, {
|
|
40
38
|
method: "HEAD",
|
|
41
|
-
signal:
|
|
39
|
+
signal: controllers[i].signal,
|
|
42
40
|
});
|
|
43
41
|
clearTimeout(timeoutId);
|
|
44
|
-
if (res.ok
|
|
45
|
-
|
|
42
|
+
if (res.ok) {
|
|
43
|
+
controllers.forEach((c) => c.abort());
|
|
46
44
|
resolve(`${gateway}/${cid}`);
|
|
47
45
|
return;
|
|
48
46
|
}
|
|
49
47
|
}
|
|
50
|
-
catch
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
successCount++;
|
|
54
|
-
// If all failed, use fallback
|
|
55
|
-
if (successCount === totalGateways && !resolved) {
|
|
56
|
-
resolved = true;
|
|
57
|
-
resolve(`${DEFAULT_IPFS_GATEWAYS[0]}/${cid}`);
|
|
48
|
+
catch {
|
|
49
|
+
clearTimeout(timeoutId);
|
|
58
50
|
}
|
|
51
|
+
if (--pending === 0)
|
|
52
|
+
reject();
|
|
59
53
|
});
|
|
60
54
|
});
|
|
55
|
+
try {
|
|
56
|
+
return await racePromise;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return `${DEFAULT_IPFS_GATEWAYS[0]}/${cid}`;
|
|
60
|
+
}
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
63
|
* Resolves a URL to an accessible HTTP(S) URL
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitizes a URL to prevent javascript: and other dangerous protocols.
|
|
3
|
+
* Returns empty string for disallowed protocols.
|
|
4
|
+
* Decodes URL-encoded characters before checking to prevent bypasses like java%73cript:
|
|
5
|
+
*/
|
|
6
|
+
export declare function sanitizeUrl(url: string | undefined | null): string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const ALLOWED_PROTOCOLS = /^(https?:\/\/|ipfs:\/\/|data:image\/)/i;
|
|
2
|
+
/**
|
|
3
|
+
* Sanitizes a URL to prevent javascript: and other dangerous protocols.
|
|
4
|
+
* Returns empty string for disallowed protocols.
|
|
5
|
+
* Decodes URL-encoded characters before checking to prevent bypasses like java%73cript:
|
|
6
|
+
*/
|
|
7
|
+
export function sanitizeUrl(url) {
|
|
8
|
+
if (!url)
|
|
9
|
+
return '';
|
|
10
|
+
const trimmed = url.trim();
|
|
11
|
+
if (!trimmed)
|
|
12
|
+
return '';
|
|
13
|
+
// Decode URL-encoded characters to catch bypasses like java%73cript:
|
|
14
|
+
let decoded;
|
|
15
|
+
try {
|
|
16
|
+
decoded = decodeURIComponent(trimmed);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
decoded = trimmed;
|
|
20
|
+
}
|
|
21
|
+
// Relative URLs and protocol-relative are fine
|
|
22
|
+
if (decoded.startsWith('/') || decoded.startsWith('./'))
|
|
23
|
+
return trimmed;
|
|
24
|
+
// Allow only safe protocols (check decoded version)
|
|
25
|
+
if (ALLOWED_PROTOCOLS.test(decoded))
|
|
26
|
+
return trimmed;
|
|
27
|
+
return '';
|
|
28
|
+
}
|
package/managed/index.js
CHANGED
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
/* export * as Registry from "./managed/registry/contract/index.cjs";
|
|
2
|
-
export * as Resolver from "./managed/resolver/contract/index.cjs";
|
|
3
|
-
export * as UnifiedDomain from "./managed/unified-domain/contract/index.cjs";
|
|
4
|
-
export * as NS from "./managed/ns/contract/index.cjs";
|
|
5
|
-
export * from "./witnesses.js";
|
|
6
|
-
export { type RegistryRecord } from "./managed/registry/contract/index.cjs";
|
|
7
|
-
export { type ResolutionData } from "./managed/resolver/contract/index.cjs";
|
|
8
|
-
export { type DomainData as UnifiedDomainData } from "./managed/unified-domain/contract/index.cjs";
|
|
9
|
-
export {
|
|
10
|
-
type DomainData,
|
|
11
|
-
type DomainReference,
|
|
12
|
-
} from "./managed/ns/contract/index.cjs";
|
|
13
|
-
*/
|
|
14
1
|
export * as Leaf from "./managed/leaf/contract/index.js";
|
|
15
2
|
export { ledger, Contract } from "./managed/leaf/contract/index.js";
|
|
3
|
+
export { domainToKey, keyToDomain } from "./utils.js";
|
|
16
4
|
//# sourceMappingURL=index.js.map
|
package/managed/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,yBAAyB,CAAC;AAChD,OAAO,EAML,MAAM,EACN,QAAQ,EACT,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
|
package/managed/leaf.compact
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
pragma language_version 0.20;
|
|
1
|
+
pragma language_version >= 0.20;
|
|
2
2
|
/// # Unshielded Nameservice
|
|
3
3
|
///
|
|
4
4
|
/// Example:
|
|
@@ -26,19 +26,19 @@ import CompactStandardLibrary;
|
|
|
26
26
|
|
|
27
27
|
// This is sealed. If the domain is sold in the parent contract
|
|
28
28
|
// The new owner should deploy a new resolver.
|
|
29
|
-
export sealed ledger PARENT_DOMAIN: Maybe<
|
|
29
|
+
export sealed ledger PARENT_DOMAIN: Maybe<Bytes<32>>;
|
|
30
30
|
export sealed ledger PARENT_RESOLVER: ContractAddress;
|
|
31
|
-
export sealed ledger DOMAIN: Maybe<
|
|
31
|
+
export sealed ledger DOMAIN: Maybe<Bytes<32>>;
|
|
32
32
|
|
|
33
33
|
export ledger DOMAIN_OWNER: ZswapCoinPublicKey;
|
|
34
34
|
// This domain could target either a wallet or a contract
|
|
35
35
|
export ledger DOMAIN_TARGET: Either<ZswapCoinPublicKey, ContractAddress>;
|
|
36
36
|
|
|
37
37
|
// Map<domain, (owner, resolver)>.
|
|
38
|
-
export ledger domains: Map<
|
|
38
|
+
export ledger domains: Map<Bytes<32>, DomainData>;
|
|
39
39
|
|
|
40
40
|
// Set of domains saved here owned by address.
|
|
41
|
-
export ledger domains_owned: Map<ZswapCoinPublicKey, Set<
|
|
41
|
+
export ledger domains_owned: Map<ZswapCoinPublicKey, Set<Bytes<32>>>;
|
|
42
42
|
|
|
43
43
|
export ledger fields: Map<Opaque<'string'>, Opaque<'string'>>;
|
|
44
44
|
|
|
@@ -51,12 +51,14 @@ struct DomainData {
|
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
constructor(
|
|
54
|
-
parent_domain: Maybe<
|
|
54
|
+
parent_domain: Maybe<Bytes<32>>,
|
|
55
55
|
parent_resolver: ContractAddress,
|
|
56
56
|
target: Either<ZswapCoinPublicKey, ContractAddress>,
|
|
57
|
-
domain: Maybe<
|
|
57
|
+
domain: Maybe<Bytes<32>>,
|
|
58
58
|
coin_color: Bytes<32>,
|
|
59
|
-
|
|
59
|
+
cost_short: Uint<128>,
|
|
60
|
+
cost_med: Uint<128>,
|
|
61
|
+
cost_long: Uint<128>,
|
|
60
62
|
kvs: Vector<10, Maybe<[Opaque<'string'>, Opaque<'string'>]>>
|
|
61
63
|
) {
|
|
62
64
|
DOMAIN_OWNER = ownPublicKey();
|
|
@@ -65,7 +67,9 @@ constructor(
|
|
|
65
67
|
DOMAIN_TARGET = disclose(target);
|
|
66
68
|
DOMAIN = disclose(domain);
|
|
67
69
|
COIN_COLOR = disclose(coin_color);
|
|
68
|
-
|
|
70
|
+
COST_SHORT = disclose(cost_short);
|
|
71
|
+
COST_MED = disclose(cost_med);
|
|
72
|
+
COST_LONG = disclose(cost_long);
|
|
69
73
|
for (const kv of kvs) {
|
|
70
74
|
if (disclose(kv) != none<[Opaque<'string'>, Opaque<'string'>]>()) {
|
|
71
75
|
fields.insert(disclose(kv.value[0]),disclose(kv.value[1]));
|
|
@@ -78,16 +82,20 @@ constructor(
|
|
|
78
82
|
// ===========================================
|
|
79
83
|
|
|
80
84
|
export ledger COIN_COLOR: Bytes<32>;
|
|
81
|
-
export ledger
|
|
85
|
+
export ledger COST_SHORT: Uint<128>; // ≤3 chars
|
|
86
|
+
export ledger COST_MED: Uint<128>; // 4 chars
|
|
87
|
+
export ledger COST_LONG: Uint<128>; // 5+ chars
|
|
82
88
|
|
|
83
|
-
export circuit update_color(c: Bytes<32>)
|
|
89
|
+
export circuit update_color(c: Bytes<32>): [] {
|
|
84
90
|
assert(DOMAIN_OWNER == ownPublicKey(), "Not the owner");
|
|
85
91
|
COIN_COLOR = disclose(c);
|
|
86
92
|
}
|
|
87
93
|
|
|
88
|
-
export circuit
|
|
94
|
+
export circuit update_costs(cost_short: Uint<128>, cost_med: Uint<128>, cost_long: Uint<128>): [] {
|
|
89
95
|
assert(DOMAIN_OWNER == ownPublicKey(), "Not the owner");
|
|
90
|
-
|
|
96
|
+
COST_SHORT = disclose(cost_short);
|
|
97
|
+
COST_MED = disclose(cost_med);
|
|
98
|
+
COST_LONG = disclose(cost_long);
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
export circuit update_target_and_fields(
|
|
@@ -104,21 +112,43 @@ export circuit update_target_and_fields(
|
|
|
104
112
|
|
|
105
113
|
}
|
|
106
114
|
|
|
107
|
-
export circuit buy_domain_for(owner: ZswapCoinPublicKey, domain:
|
|
115
|
+
export circuit buy_domain_for(owner: ZswapCoinPublicKey, domain: Bytes<32>, len: Uint<32>, resolver: ContractAddress): [] {
|
|
116
|
+
assert(len <= 32, "len must be <= 32");
|
|
117
|
+
assert(len >= 1, "domain name cannot be empty");
|
|
118
|
+
|
|
119
|
+
const valid = fold(
|
|
120
|
+
(acc: Boolean, byte: Uint<8>, i: Uint<8>): Boolean =>
|
|
121
|
+
acc && (((i as Uint<32>) < len) || (byte == 255)),
|
|
122
|
+
true, domain,
|
|
123
|
+
[0, 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]
|
|
124
|
+
);
|
|
125
|
+
assert(valid, "Invalid domain key padding");
|
|
126
|
+
|
|
108
127
|
assert(!domains.member(disclose(domain)), "Domain already exists");
|
|
109
128
|
|
|
110
|
-
//
|
|
111
|
-
|
|
129
|
+
// pricing: shared color, per-tier cost
|
|
130
|
+
const d_len = disclose(len);
|
|
131
|
+
if (d_len <= 3) {
|
|
132
|
+
receiveUnshielded(COIN_COLOR, COST_SHORT);
|
|
133
|
+
sendUnshielded(COIN_COLOR, COST_SHORT, right<ContractAddress, UserAddress>(UserAddress { bytes: DOMAIN_OWNER.bytes }));
|
|
134
|
+
} else if (d_len == 4) {
|
|
135
|
+
receiveUnshielded(COIN_COLOR, COST_MED);
|
|
136
|
+
sendUnshielded(COIN_COLOR, COST_MED, right<ContractAddress, UserAddress>(UserAddress { bytes: DOMAIN_OWNER.bytes }));
|
|
137
|
+
} else {
|
|
138
|
+
receiveUnshielded(COIN_COLOR, COST_LONG);
|
|
139
|
+
sendUnshielded(COIN_COLOR, COST_LONG, right<ContractAddress, UserAddress>(UserAddress { bytes: DOMAIN_OWNER.bytes }));
|
|
140
|
+
}
|
|
112
141
|
|
|
113
142
|
const d_owner = disclose(owner);
|
|
114
143
|
const domain_data = DomainData {
|
|
115
144
|
d_owner,
|
|
116
145
|
disclose(resolver)
|
|
117
146
|
};
|
|
118
|
-
|
|
119
|
-
|
|
147
|
+
domains.insert(disclose(domain), disclose(domain_data));
|
|
148
|
+
|
|
149
|
+
// Track domains owned by address
|
|
120
150
|
if (!domains_owned.member(d_owner)) {
|
|
121
|
-
domains_owned.insert(d_owner, default<Set<
|
|
151
|
+
domains_owned.insert(d_owner, default<Set<Bytes<32>>>);
|
|
122
152
|
}
|
|
123
153
|
domains_owned.lookup(d_owner).insert(disclose(domain));
|
|
124
154
|
}
|
|
@@ -142,12 +172,23 @@ export circuit clear_field(k: Opaque<'string'>): [] {
|
|
|
142
172
|
fields.remove(disclose(k));
|
|
143
173
|
}
|
|
144
174
|
|
|
145
|
-
export circuit clear_all_fields(
|
|
175
|
+
export circuit clear_all_fields(): [] {
|
|
146
176
|
assert(DOMAIN_OWNER == ownPublicKey(), "Not the domain owner");
|
|
147
177
|
fields.resetToDefault();
|
|
148
178
|
}
|
|
149
179
|
|
|
150
|
-
export circuit register_domain_for(owner: ZswapCoinPublicKey, domain:
|
|
180
|
+
export circuit register_domain_for(owner: ZswapCoinPublicKey, domain: Bytes<32>, len: Uint<32>, resolver: ContractAddress): [] {
|
|
181
|
+
assert(len <= 32, "len must be <= 32");
|
|
182
|
+
assert(len >= 1, "domain name cannot be empty");
|
|
183
|
+
|
|
184
|
+
const valid = fold(
|
|
185
|
+
(acc: Boolean, byte: Uint<8>, i: Uint<8>): Boolean =>
|
|
186
|
+
acc && (((i as Uint<32>) < len) || (byte == 255)),
|
|
187
|
+
true, domain,
|
|
188
|
+
[0, 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]
|
|
189
|
+
);
|
|
190
|
+
assert(valid, "Invalid domain key padding");
|
|
191
|
+
|
|
151
192
|
assert(!domains.member(disclose(domain)), "Domain already exists");
|
|
152
193
|
assert(DOMAIN_OWNER == ownPublicKey(), "Not the domain owner");
|
|
153
194
|
|
|
@@ -161,12 +202,12 @@ export circuit register_domain_for(owner: ZswapCoinPublicKey, domain: Opaque<'st
|
|
|
161
202
|
|
|
162
203
|
// Track domains owned by address
|
|
163
204
|
if (!domains_owned.member(d_owner)) {
|
|
164
|
-
domains_owned.insert(d_owner, default<Set<
|
|
205
|
+
domains_owned.insert(d_owner, default<Set<Bytes<32>>>);
|
|
165
206
|
}
|
|
166
207
|
domains_owned.lookup(d_owner).insert(disclose(domain));
|
|
167
208
|
}
|
|
168
209
|
|
|
169
|
-
export circuit set_resolver(domain:
|
|
210
|
+
export circuit set_resolver(domain: Bytes<32>, resolver: ContractAddress): [] {
|
|
170
211
|
assert(domains.member(disclose(domain)), "Domain does not exist");
|
|
171
212
|
|
|
172
213
|
const current_data = domains.lookup(disclose(domain));
|
|
@@ -186,7 +227,7 @@ export circuit update_domain_target(new_target: Either<ZswapCoinPublicKey, Contr
|
|
|
186
227
|
DOMAIN_TARGET = disclose(new_target);
|
|
187
228
|
}
|
|
188
229
|
|
|
189
|
-
export circuit transfer_domain(domain:
|
|
230
|
+
export circuit transfer_domain(domain: Bytes<32>, new_owner: ZswapCoinPublicKey): [] {
|
|
190
231
|
assert(domains.member(disclose(domain)), "Domain does not exist");
|
|
191
232
|
|
|
192
233
|
const current_data = domains.lookup(disclose(domain));
|
|
@@ -202,7 +243,7 @@ export circuit transfer_domain(domain: Opaque<'string'>, new_owner: ZswapCoinPub
|
|
|
202
243
|
// Update ownership tracking
|
|
203
244
|
domains_owned.lookup(ownPublicKey()).remove(disclose(domain));
|
|
204
245
|
if (!domains_owned.member(disclose(new_owner))) {
|
|
205
|
-
domains_owned.insert(disclose(new_owner), default<Set<
|
|
246
|
+
domains_owned.insert(disclose(new_owner), default<Set<Bytes<32>>>);
|
|
206
247
|
}
|
|
207
248
|
domains_owned.lookup(disclose(new_owner)).insert(disclose(domain));
|
|
208
249
|
}
|
|
@@ -216,12 +257,12 @@ export circuit change_owner(new_owner: ZswapCoinPublicKey) : [] {
|
|
|
216
257
|
|
|
217
258
|
/*
|
|
218
259
|
Read only, but not pure, so let's not use those
|
|
219
|
-
export circuit resolve(domain:
|
|
260
|
+
export circuit resolve(domain: Bytes<32>): DomainData {
|
|
220
261
|
assert(domains.member(disclose(domain)), "Domain does not exist");
|
|
221
262
|
return domains.lookup(disclose(domain));
|
|
222
263
|
}
|
|
223
264
|
|
|
224
|
-
export circuit get_parent_domain(): Maybe<
|
|
265
|
+
export circuit get_parent_domain(): Maybe<Bytes<32>> {
|
|
225
266
|
return PARENT_DOMAIN;
|
|
226
267
|
}
|
|
227
268
|
|
|
@@ -229,7 +270,7 @@ export circuit get_contract_owner(): ZswapCoinPublicKey {
|
|
|
229
270
|
return DOMAIN_OWNER;
|
|
230
271
|
}
|
|
231
272
|
|
|
232
|
-
export circuit get_domain(): Maybe<
|
|
273
|
+
export circuit get_domain(): Maybe<Bytes<32>> {
|
|
233
274
|
return DOMAIN;
|
|
234
275
|
}
|
|
235
276
|
|
|
@@ -241,7 +282,7 @@ export circuit get_parent_resolver(): ContractAddress {
|
|
|
241
282
|
return PARENT_RESOLVER;
|
|
242
283
|
}
|
|
243
284
|
|
|
244
|
-
export circuit addr_owns_domain(addr: ZswapCoinPublicKey, domain:
|
|
285
|
+
export circuit addr_owns_domain(addr: ZswapCoinPublicKey, domain: Bytes<32>): Boolean {
|
|
245
286
|
assert(domains_owned.member(disclose(addr)), "Address does not own domains in this register");
|
|
246
287
|
return domains_owned.lookup(disclose(addr)).member(disclose(domain));
|
|
247
288
|
}
|