@decentrys/ui-sdk 0.1.0 → 0.1.2
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/README.md +198 -24
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @decentrys/ui-sdk
|
|
2
2
|
|
|
3
|
-
Drop-in React components
|
|
3
|
+
**Drop-in React components that render a Decentrys assessment — built so a new project can never be shown as dangerous for being new.**
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,44 +8,218 @@ Drop-in React components for rendering Decentrys risk assessments.
|
|
|
8
8
|
npm install @decentrys/ui-sdk @decentrys/protect
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
React 18 or 19, as a peer dependency
|
|
11
|
+
React 18 or 19, as a peer dependency alongside `react-dom`. Never bundled — two
|
|
12
|
+
copies of React in one app is a real breakage. No styling dependency, no font
|
|
13
|
+
fetch, no network calls of its own.
|
|
12
14
|
|
|
13
|
-
##
|
|
15
|
+
## Quick start
|
|
14
16
|
|
|
15
17
|
```tsx
|
|
16
|
-
import {
|
|
18
|
+
import { useEffect, useState } from 'react';
|
|
19
|
+
import { Decentrys, type ProtectResult, type TransactionRequest } from '@decentrys/protect';
|
|
20
|
+
import { DecentrysUiProvider, TransactionRiskBanner, RiskDetailsModal } from '@decentrys/ui-sdk';
|
|
21
|
+
|
|
22
|
+
const decentrys = new Decentrys({ apiKey: 'dk_pub_live_...' });
|
|
23
|
+
|
|
24
|
+
export function SigningScreen({ tx, onSign, onCancel }: {
|
|
25
|
+
tx: TransactionRequest;
|
|
26
|
+
onSign: () => void;
|
|
27
|
+
onCancel: () => void;
|
|
28
|
+
}) {
|
|
29
|
+
const [result, setResult] = useState<ProtectResult | null>(null);
|
|
30
|
+
const [detailsOpen, setDetailsOpen] = useState(false);
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
let live = true;
|
|
34
|
+
decentrys.assessTransaction(tx).then((r) => { if (live) setResult(r); });
|
|
35
|
+
return () => { live = false; };
|
|
36
|
+
}, [tx]);
|
|
37
|
+
|
|
38
|
+
if (!result) return null;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<DecentrysUiProvider theme={{ surface: '#0b0d10', text: '#e8ebef', radius: '12px' }}>
|
|
42
|
+
<TransactionRiskBanner
|
|
43
|
+
result={result}
|
|
44
|
+
onViewDetails={() => setDetailsOpen(true)}
|
|
45
|
+
actions={
|
|
46
|
+
<>
|
|
47
|
+
{/* Your buttons, your policy. This package never disables them. */}
|
|
48
|
+
<button onClick={onSign}>Confirm</button>
|
|
49
|
+
<button onClick={onCancel}>Cancel</button>
|
|
50
|
+
</>
|
|
51
|
+
}
|
|
52
|
+
/>
|
|
53
|
+
<RiskDetailsModal result={result} open={detailsOpen} onClose={() => setDetailsOpen(false)} />
|
|
54
|
+
</DecentrysUiProvider>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`DecentrysUiProvider` sets theme and copy once for everything beneath it and
|
|
60
|
+
emits the stylesheet a single time. Without a provider every component emits
|
|
61
|
+
its own — so a single `<AddressRiskBadge>` dropped into a page still renders
|
|
62
|
+
correctly.
|
|
63
|
+
|
|
64
|
+
Placing the CSS yourself instead — in a `<head>`, a bundled stylesheet or a
|
|
65
|
+
shadow root — is `<DecentrysUiProvider injectStyles={false}>`, or export the
|
|
66
|
+
string directly:
|
|
17
67
|
|
|
68
|
+
```tsx
|
|
69
|
+
import { DECENTRYS_UI_CSS } from '@decentrys/ui-sdk';
|
|
18
70
|
<style>{DECENTRYS_UI_CSS}</style>
|
|
19
|
-
<TransactionRiskBanner result={result} actions={<YourButtons />} />
|
|
20
71
|
```
|
|
21
72
|
|
|
22
|
-
##
|
|
73
|
+
## Components
|
|
74
|
+
|
|
75
|
+
Note which prop each one takes — some want the whole `ProtectResult`, some want
|
|
76
|
+
a piece of it.
|
|
77
|
+
|
|
78
|
+
| Component | Required props | Use it when |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `TransactionRiskBanner` | `result` | A user is about to sign. The main one. |
|
|
81
|
+
| `ApprovalWarning` | `result`, `approval` | A token approval — leads with what is being granted, not a score. |
|
|
82
|
+
| `ContractFacts` | `assessment` | Showing what a contract can do (upgrade, mint, pause). |
|
|
83
|
+
| `TokenSecurityPanel` | `result` | A token detail screen. |
|
|
84
|
+
| `ThreatSignalList` | `signals` | You want signals only, in your own layout. |
|
|
85
|
+
| `AddressRiskBadge` | `assessment` **or** `level` | Compact inline badge beside an address. |
|
|
86
|
+
| `RiskDetailsModal` | `result`, `open`, `onClose` | "Why?" — every fact, signal, evidence item and score component. |
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
// An approval. `approval` describes what is being granted; the SDK does not
|
|
90
|
+
// infer it from the assessment.
|
|
91
|
+
<ApprovalWarning
|
|
92
|
+
result={approvalResult} // from decentrys.screenApproval(...)
|
|
93
|
+
approval={{
|
|
94
|
+
token: '0xa0b8…',
|
|
95
|
+
symbol: 'USDC',
|
|
96
|
+
decimals: 6,
|
|
97
|
+
spender: '0x1111…',
|
|
98
|
+
spenderLabel: '1inch Router',
|
|
99
|
+
amount: 'unlimited', // base units, or the literal 'unlimited'
|
|
100
|
+
}}
|
|
101
|
+
/>
|
|
102
|
+
// "This grants 1inch Router permission to spend an unlimited amount of your USDC,
|
|
103
|
+
// now and at any time in the future, until it is revoked."
|
|
104
|
+
//
|
|
105
|
+
// Omit `amount` and it says the allowance could not be decoded — never that it
|
|
106
|
+
// is small. Omit `decimals` and the figure is labelled as base units rather
|
|
107
|
+
// than silently shown 10^18 times too small.
|
|
108
|
+
|
|
109
|
+
<ContractFacts assessment={result.assessment} demotedSignals={result.demotedSignals} />
|
|
110
|
+
|
|
111
|
+
<TokenSecurityPanel result={result} tokenSymbol="USDC" showComponents />
|
|
112
|
+
|
|
113
|
+
<ThreatSignalList signals={result.assessment.threatSignals} />
|
|
114
|
+
|
|
115
|
+
<AddressRiskBadge assessment={result.assessment} address="0x1111…" />
|
|
116
|
+
<AddressRiskBadge level="CAUTION" /> {/* when you only have the level */}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Every component also accepts `className`, `style`, `theme`, `copy` and
|
|
120
|
+
`levelLabels`. `AddressRiskBadge` takes exactly one of `assessment` or `level`,
|
|
121
|
+
enforced by the type rather than a runtime throw — a component that throws
|
|
122
|
+
inside a wallet's render tree takes that wallet's screen down.
|
|
123
|
+
|
|
124
|
+
## What these will not do
|
|
125
|
+
|
|
126
|
+
**Nothing blocks.** `DecisionPresentation.blocksUi` is typed as the literal
|
|
127
|
+
`false`, including for a `block` policy action. No control here is ever
|
|
128
|
+
rendered `disabled` — the decision belongs to your application, and you wire
|
|
129
|
+
your own buttons through `actions`.
|
|
23
130
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
the
|
|
131
|
+
**`LIMITED` history is never a warning.** `HistoryPresentation` types `tone` as
|
|
132
|
+
the literal `'neutral'` and `isWarning` as the literal `false`, so rendering it
|
|
133
|
+
as an alert doesn't compile. It's the correct, expected state for anything
|
|
134
|
+
recently deployed.
|
|
27
135
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
136
|
+
**Facts never look like threat signals.** `fact`, `capability` and `unknown`
|
|
137
|
+
are separate tone families from the risk tones, each row carries its kind as a
|
|
138
|
+
word and its own non-colour glyph, and a stale or low-confidence signal that
|
|
139
|
+
did not raise the level is toned `inactive`. Colour is the third carrier of
|
|
140
|
+
meaning, not the first — a colour-blind user still has to be able to tell
|
|
141
|
+
"deployed three days ago" from "attributed to a drainer family".
|
|
32
142
|
|
|
33
|
-
**
|
|
34
|
-
|
|
35
|
-
not the first — a colour-blind user still has to be able to tell "deployed
|
|
36
|
-
three days ago" from "attributed to a drainer family".
|
|
143
|
+
**Anything above zero hops says so in a sentence**, not just a badge. A hop is
|
|
144
|
+
an inference about a counterparty, and a badge reading "2" tells nobody that.
|
|
37
145
|
|
|
38
146
|
## Theming
|
|
39
147
|
|
|
40
|
-
CSS custom properties
|
|
41
|
-
|
|
42
|
-
|
|
148
|
+
31 CSS custom properties. Pass them by their **bare token name** — the
|
|
149
|
+
`--dcy-` prefix is added for you:
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
<TransactionRiskBanner
|
|
153
|
+
result={result}
|
|
154
|
+
theme={{ surface: '#1a1a2e', radius: '12px', 'tone-caution': '#ab9ff2' }}
|
|
155
|
+
className="my-wallet-card"
|
|
156
|
+
/>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The tokens (`THEME_TOKENS` is exported, and `DecentrysTheme` is
|
|
160
|
+
`Partial<Record<ThemeToken, string>>`):
|
|
161
|
+
|
|
162
|
+
- Layout and type — `font`, `font-mono`, `radius`, `gap`
|
|
163
|
+
- Surfaces and text — `surface`, `surface-raised`, `border`, `text`, `text-muted`, `text-faint`, `focus`
|
|
164
|
+
- A foreground and a `-bg` wash for each of ten tones — `tone-neutral`, `tone-info`, `tone-caution`, `tone-elevated`, `tone-high`, `tone-critical`, `tone-fact`, `tone-capability`, `tone-unknown`, `tone-inactive`
|
|
165
|
+
|
|
166
|
+
Inline `theme` is applied on the component's own root, so it beats both the
|
|
167
|
+
defaults and the `prefers-color-scheme` dark block — a fixed brand palette
|
|
168
|
+
doesn't flip on a user's OS setting. Props win over the provider, so one banner
|
|
169
|
+
can be re-themed inside an otherwise uniform tree.
|
|
170
|
+
|
|
171
|
+
Every meaningful element also carries a `data-dcy-*` attribute
|
|
172
|
+
(`data-dcy-level`, `data-dcy-kind`, `data-dcy-tone`) if you'd rather style from
|
|
173
|
+
your own CSS.
|
|
174
|
+
|
|
175
|
+
## Copy and translation
|
|
176
|
+
|
|
177
|
+
Every string is overridable, on the provider or per component:
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
<DecentrysUiProvider copy={{ /* Partial<UiCopy> */ }}>
|
|
181
|
+
<TransactionRiskBanner result={result} levelLabels={{ CAUTION: 'Attention' }} />
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
`DEFAULT_COPY` and `resolveCopy` are exported.
|
|
185
|
+
|
|
186
|
+
## Building your own presentation
|
|
187
|
+
|
|
188
|
+
The presentation logic is pure, React-free and separately exported, so a
|
|
189
|
+
renderer you write yourself cannot drift from the one here:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import {
|
|
193
|
+
levelPresentation, factPresentation, capabilityPresentation, signalPresentation,
|
|
194
|
+
historyPresentation, unknownPresentation, approvalPresentation, assessmentSections,
|
|
195
|
+
componentRows, decisionPresentation, evidencePresentation, availabilityPresentation,
|
|
196
|
+
hopPresentation, formatConfidence, formatTokenAmount, shortenAddress,
|
|
197
|
+
MIN_RAISING_CONFIDENCE, RISK_LEVEL_LABEL,
|
|
198
|
+
} from '@decentrys/ui-sdk';
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`MIN_RAISING_CONFIDENCE` is re-exported from `@decentrys/protect` rather than
|
|
202
|
+
copied, so a renderer cannot put a signal in the "did not raise the level" list
|
|
203
|
+
while the classifier was in fact raising it.
|
|
204
|
+
|
|
205
|
+
## React Native
|
|
206
|
+
|
|
207
|
+
These render React DOM and won't work in RN. The presentation logic above is
|
|
208
|
+
React-free and reusable as-is.
|
|
209
|
+
|
|
210
|
+
## The rest of the SDK
|
|
211
|
+
|
|
212
|
+
| Package | For |
|
|
213
|
+
|---|---|
|
|
214
|
+
| [`@decentrys/protect`](https://www.npmjs.com/package/@decentrys/protect) | Pre-sign risk assessment for wallets and dapps |
|
|
215
|
+
| [`@decentrys/ui-sdk`](https://www.npmjs.com/package/@decentrys/ui-sdk) | React components that render Protect results |
|
|
216
|
+
| [`@decentrys/sentinel-sdk`](https://www.npmjs.com/package/@decentrys/sentinel-sdk) | Monitoring deployed contracts and treasuries |
|
|
217
|
+
| [`@decentrys/risk-sdk`](https://www.npmjs.com/package/@decentrys/risk-sdk) | Screening for exchanges and custodians |
|
|
218
|
+
| [`@decentrys/dri-sdk`](https://www.npmjs.com/package/@decentrys/dri-sdk) | Fund tracing and recovery intelligence |
|
|
219
|
+
| [`@decentrys/agent`](https://www.npmjs.com/package/@decentrys/agent) | Policy enforcement for autonomous agents |
|
|
43
220
|
|
|
44
221
|
## Licence
|
|
45
222
|
|
|
46
223
|
MIT © Decentrys Labs
|
|
47
224
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
- [decentrys.com](https://decentrys.com) · [SDK overview](https://decentrys.com/sdk) · [Developer API](https://decentrys.com/developers)
|
|
51
|
-
- Source: [github.com/teamdecentrys-byte/Decentrys](https://github.com/teamdecentrys-byte/Decentrys)
|
|
225
|
+
[decentrys.com](https://decentrys.com) · [SDK overview](https://decentrys.com/sdk) · [Developer API](https://decentrys.com/developers) · [Source](https://github.com/teamdecentrys-byte/Decentrys)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentrys/ui-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Drop-in React components for rendering Decentrys risk assessments, built so a new project can never be shown as dangerous for being new.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Decentrys Labs",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"README.md"
|
|
50
50
|
],
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@decentrys/protect": "0.1.
|
|
52
|
+
"@decentrys/protect": "0.1.2"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"react": ">=18",
|