solid-drift 0.14.0 → 0.16.0
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 +61 -0
- package/dist/fun.d.ts +87 -0
- package/dist/fun.js +143 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/web3data.d.ts +323 -0
- package/dist/web3data.js +751 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1094,6 +1094,37 @@ const connect = createConnectButton(() => btn, { strength: 0.35 });
|
|
|
1094
1094
|
|
|
1095
1095
|
Returns `{ copyTick, chainPulse, status }`. `status()` is `"idle"`, `"ticking"` (check visible), or `"pulsing"` (ring expanding). Call `chainPulse()` after a successful connection or network switch. Under reduced motion there is no magnetic pull or scale; `copyTick()` and `chainPulse()` still show their overlays statically.
|
|
1096
1096
|
|
|
1097
|
+
### Web3 data layer
|
|
1098
|
+
|
|
1099
|
+
A zero-dependency read layer for chain and market data as signals: public RPC and API endpoints over `fetch`, with user-swappable endpoints. Every network primitive shares the `{ data, error, status, retry, abort }` shape, is SSR-safe (nothing fetches on the server), and polls with error backoff. Defaults are conservative because public endpoints are rate-limited. This is read-only: transaction signing stays with wallet libraries.
|
|
1100
|
+
|
|
1101
|
+
```tsx
|
|
1102
|
+
import {
|
|
1103
|
+
createPoll, createTokenPrice, createPriceChange, createPriceCompare,
|
|
1104
|
+
createGasPrice, createBalance, createTxReceipt, createBlockNumber,
|
|
1105
|
+
createChainlinkPrice, createNFTMetadata, createENS, createIdenticon,
|
|
1106
|
+
createChain, CHAINS, shortenAddress, isAddress, formatUnits, parseUnits,
|
|
1107
|
+
} from "solid-drift";
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
**Polling infra.** `createPoll(fetcher, options?)` fetches immediately (unless `immediate: false`), then on `interval` (default 30s). On error the interval multiplies by `backoff` (default 2) up to `maxInterval` (default 5min) and resets on the next success. Returns `{ data, error, status, retry, abort }`; `status()` is `"idle"`, `"loading"`, `"success"`, or `"error"`.
|
|
1111
|
+
|
|
1112
|
+
**Pure helpers.** `isAddress(value)` checks `0x` + 40 hex chars. `shortenAddress(address, chars = 4)` renders `0xd8dA…6045` and passes invalid input through. `formatUnits(value, decimals = 18)` formats wei-style bigints as decimal strings without float artifacts; `parseUnits(value, decimals = 18)` parses them back and throws on invalid input. `CHAINS` maps seven chain ids (Ethereum, Optimism, BNB Chain, Polygon, Base, Arbitrum One, Sepolia) to name, currency, decimals, explorer, and a public RPC; `createChain(id)` looks one up as a reactive accessor (`undefined` for unknown ids).
|
|
1113
|
+
|
|
1114
|
+
**Market.** `createTokenPrice(tokenId, options?)` polls CoinGecko's public API (default 60s; swap `endpoint` or `vsCurrency`) and exposes `price()` and `change24h()`. `createPriceChange(source, options?)` samples any numeric signal on change and on `sampleMs` (default 60s), keeps a rolling `windowMs` (default 1h), and reports the percent change between the first and last sample; `reset()` clears the window. `createPriceCompare(a, b)` compares two price signals with `ratio()`, `diffPercent()`, and `leader()` (`"a"`, `"b"`, or `"tie"`).
|
|
1115
|
+
|
|
1116
|
+
**Chain (JSON-RPC).** `createGasPrice(options?)` reads `eth_gasPrice` every 15s as `{ wei, gwei }`. `createBalance(address, options?)` reads the native balance every 20s, or an ERC20 `balanceOf` when `token` is set, exposing `balance()` (bigint) and `formatted()`. `createTxReceipt(hash, options?)` polls every 4s until the receipt lands, then stops on its own; `mined()` mirrors that and `receipt()` carries `transactionHash`, `blockNumber`, `success`, and `gasUsed`. `createBlockNumber(options?)` polls the latest block every 12s as a chain-health heartbeat. `createChainlinkPrice(feed, options?)` reads a Chainlink `AggregatorV3Interface` feed on-chain (`decimals()` once, then `latestRoundData()` every 30s). All take an `endpoint` option defaulting to a public mainnet RPC.
|
|
1117
|
+
|
|
1118
|
+
**Identity and NFTs.** `createNFTMetadata(contract, tokenId, options?)` fetches `tokenURI` on-chain, resolves the JSON (one-shot with `retry`), rewrites `ipfs://` through a gateway, and exposes `metadata()` (`name`, `description`, `image`, `attributes`, `raw`) plus `image()`. `createENS(address, options?)` reverse-resolves an address through the public ENS registry (one-shot with `retry`); `name()` is `undefined` when no name is set. `createIdenticon(address, options?)` renders a deterministic mirrored-grid SVG avatar as a data URI, pure computation, works on the server.
|
|
1119
|
+
|
|
1120
|
+
```tsx
|
|
1121
|
+
const { price, change24h } = createTokenPrice("ethereum");
|
|
1122
|
+
const { change } = createPriceChange(price);
|
|
1123
|
+
const { formatted } = createBalance("0xd8dA…6045");
|
|
1124
|
+
const { mined, receipt } = createTxReceipt("0x5c50…f7b");
|
|
1125
|
+
const avatar = createIdenticon("0xd8dA…6045");
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1097
1128
|
### `createToast(options?)`
|
|
1098
1129
|
|
|
1099
1130
|
A signal-native toast queue with choreographed lifecycle. The primitive owns timing and state; you own the rendering, so no component opinions leak into your design system. Each toast moves through `"entering"` to `"visible"` to `"leaving"` to removed on the shared animation clock: bind `state` to CSS classes or drift values for enter/exit motion without any timers of your own.
|
|
@@ -1164,6 +1195,36 @@ const machine = createSlotMachine({
|
|
|
1164
1195
|
|
|
1165
1196
|
Options: `symbols` (required, at least 2), `reels` (default `3`), `duration` (ms for the first reel, default `1400`), `stagger` (extra ms per subsequent reel, default `500`), `minSpins` (full rotations before stopping, default `3`), `easing` (default `"easeOutQuart"`), `onTick(reel, symbol)`, `onDone(result)`. Returns `{ values, result, status, spin, stop, reset }`: `values()` is the visible symbol per reel, `result()` the final symbols of the last spin, `status()` is `"idle"`, `"spinning"`, or `"done"`. `stop()` halts at the current symbols; `reset()` returns to idle. SSR-safe and reduced-motion safe: `spin()` jumps straight to the result.
|
|
1166
1197
|
|
|
1198
|
+
### `createRedPacket(options?)`
|
|
1199
|
+
|
|
1200
|
+
Crypto red packet ceremony: tap to open, coins burst out with physics, the amount counts up. The primitive owns the ceremony state machine (`"sealed"`, `"opening"`, `"bursting"`, `"revealed"`) and the coin particle physics; you render the envelope and the coins. Each coin carries position, rotation, size, opacity, and its share of the total, split randomly like a real red packet grab.
|
|
1201
|
+
|
|
1202
|
+
```tsx
|
|
1203
|
+
import { createRedPacket } from "solid-drift"
|
|
1204
|
+
|
|
1205
|
+
const packet = createRedPacket({ amount: 88, coins: 14 })
|
|
1206
|
+
|
|
1207
|
+
<button onClick={() => packet.open()}>
|
|
1208
|
+
{packet.status() === "sealed"
|
|
1209
|
+
? "🧧 Tap to open"
|
|
1210
|
+
: `$${packet.revealed().toFixed(2)}`}
|
|
1211
|
+
</button>
|
|
1212
|
+
<For each={packet.coins()}>
|
|
1213
|
+
{(coin) => (
|
|
1214
|
+
<div
|
|
1215
|
+
class="coin"
|
|
1216
|
+
style={{
|
|
1217
|
+
transform: `translate(${coin.x}px, ${coin.y}px) rotate(${coin.rotation}deg)`,
|
|
1218
|
+
opacity: coin.opacity,
|
|
1219
|
+
width: `${coin.size}px`,
|
|
1220
|
+
}}
|
|
1221
|
+
/>
|
|
1222
|
+
)}
|
|
1223
|
+
</For>
|
|
1224
|
+
```
|
|
1225
|
+
|
|
1226
|
+
Options: `coins` (default `12`), `amount` (total, default `88`), `spread` (burst size in px, default `160`), `gravity` (px/s^2, default `900`), `openDuration` (ms, default `500`), `burstDuration` (ms, default `1600`), `revealDuration` (ms, default `800`), `onOpen`, `onReveal(amount)`. Returns `{ status, coins, revealed, open, reset }`. SSR-safe and reduced-motion safe: `open()` jumps straight to revealed with no burst.
|
|
1227
|
+
|
|
1167
1228
|
### Easings
|
|
1168
1229
|
|
|
1169
1230
|
Named easings: `linear`, `easeInQuad`, `easeOutQuad`, `easeInOutQuad`, `easeInCubic`, `easeOutCubic`, `easeInOutCubic`, `easeInQuart`, `easeOutQuart`, `easeInOutQuart`, `easeOutExpo`, `easeOutBack`, plus the cartoon set: `easeInBack` (anticipation dip before movement), `easeInOutBack` (wind-up, overshoot, settle), `easeOutElastic` (decaying rubber-band oscillation), `easeOutBounce` (shrinking cartoon bounces). Also `cubicBezier(x1, y1, x2, y2)` for CSS-style curves. Pass a name or a custom `(t) => number` function anywhere an easing is accepted.
|
package/dist/fun.d.ts
CHANGED
|
@@ -62,3 +62,90 @@ export interface SlotMachineControls<T = string> {
|
|
|
62
62
|
* ```
|
|
63
63
|
*/
|
|
64
64
|
export declare function createSlotMachine<T = string>(options: SlotMachineOptions<T>): SlotMachineControls<T>;
|
|
65
|
+
export type RedPacketStatus = "sealed" | "opening" | "bursting" | "revealed";
|
|
66
|
+
export interface RedPacketCoin {
|
|
67
|
+
id: number;
|
|
68
|
+
/** Pixels right from the packet center. */
|
|
69
|
+
x: number;
|
|
70
|
+
/** Pixels down from the packet center. */
|
|
71
|
+
y: number;
|
|
72
|
+
vx: number;
|
|
73
|
+
vy: number;
|
|
74
|
+
/** Degrees. */
|
|
75
|
+
rotation: number;
|
|
76
|
+
/** Degrees per second. */
|
|
77
|
+
vr: number;
|
|
78
|
+
/** Diameter in pixels. */
|
|
79
|
+
size: number;
|
|
80
|
+
/** 0..1, fades at the end of life. */
|
|
81
|
+
opacity: number;
|
|
82
|
+
/** This coin's share of the total amount. */
|
|
83
|
+
amount: number;
|
|
84
|
+
}
|
|
85
|
+
export interface RedPacketOptions {
|
|
86
|
+
/** Coins in the burst. Default 12. */
|
|
87
|
+
coins?: number;
|
|
88
|
+
/** Total amount, split randomly across coins like a real red packet. Default 88. */
|
|
89
|
+
amount?: number;
|
|
90
|
+
/** Burst size in pixels, scales launch speed. Default 160. */
|
|
91
|
+
spread?: number;
|
|
92
|
+
/** Gravity in px/s^2. Default 900. */
|
|
93
|
+
gravity?: number;
|
|
94
|
+
/** Envelope opening ceremony in ms. Default 500. */
|
|
95
|
+
openDuration?: number;
|
|
96
|
+
/** Coin burst in ms before the reveal. Default 1600. */
|
|
97
|
+
burstDuration?: number;
|
|
98
|
+
/** Amount count-up in ms once revealed. Default 800. */
|
|
99
|
+
revealDuration?: number;
|
|
100
|
+
/** Called when the packet is tapped open. */
|
|
101
|
+
onOpen?: () => void;
|
|
102
|
+
/** Called with the total amount when the reveal lands. */
|
|
103
|
+
onReveal?: (amount: number) => void;
|
|
104
|
+
}
|
|
105
|
+
export interface RedPacketControls {
|
|
106
|
+
status: Accessor<RedPacketStatus>;
|
|
107
|
+
/** Live coin particles, centered on the packet. Render them absolutely. */
|
|
108
|
+
coins: Accessor<RedPacketCoin[]>;
|
|
109
|
+
/** Amount counted up so far. Reaches the total when revealed. */
|
|
110
|
+
revealed: Accessor<number>;
|
|
111
|
+
/** Tap the packet: sealed -> opening -> bursting -> revealed. */
|
|
112
|
+
open: () => void;
|
|
113
|
+
/** Back to sealed. */
|
|
114
|
+
reset: () => void;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Crypto red packet ceremony: tap to open, coins burst out with physics,
|
|
118
|
+
* the amount counts up. The hongbao moment, as a signal-native primitive.
|
|
119
|
+
*
|
|
120
|
+
* The primitive owns the ceremony state machine and the coin particle
|
|
121
|
+
* physics; you render the envelope and the coins however fits your design.
|
|
122
|
+
* Each coin carries position, rotation, size, opacity, and its share of
|
|
123
|
+
* the total amount, split randomly like a real red packet grab.
|
|
124
|
+
*
|
|
125
|
+
* SSR-safe: `open()` jumps straight to revealed on the server. Under
|
|
126
|
+
* reduced motion the packet opens instantly with no burst: the amount
|
|
127
|
+
* just appears.
|
|
128
|
+
*
|
|
129
|
+
* ```tsx
|
|
130
|
+
* import { createRedPacket } from "solid-drift"
|
|
131
|
+
*
|
|
132
|
+
* const packet = createRedPacket({ amount: 88, coins: 14 })
|
|
133
|
+
*
|
|
134
|
+
* <button onClick={() => packet.open()}>
|
|
135
|
+
* {packet.status() === "sealed" ? "🧧 Tap to open" : `$${packet.revealed().toFixed(2)}`}
|
|
136
|
+
* </button>
|
|
137
|
+
* <For each={packet.coins()}>
|
|
138
|
+
* {(coin) => (
|
|
139
|
+
* <div
|
|
140
|
+
* class="coin"
|
|
141
|
+
* style={{
|
|
142
|
+
* transform: `translate(${coin.x}px, ${coin.y}px) rotate(${coin.rotation}deg)`,
|
|
143
|
+
* opacity: coin.opacity,
|
|
144
|
+
* width: `${coin.size}px`,
|
|
145
|
+
* }}
|
|
146
|
+
* />
|
|
147
|
+
* )}
|
|
148
|
+
* </For>
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
export declare function createRedPacket(options?: RedPacketOptions): RedPacketControls;
|
package/dist/fun.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createSignal, onCleanup, } from "solid-js";
|
|
2
2
|
import { now, schedule } from "./engine.js";
|
|
3
3
|
import { resolveEasing } from "./easing.js";
|
|
4
|
+
import { createTween } from "./tween.js";
|
|
4
5
|
import { prefersReducedMotion } from "./reduced-motion.js";
|
|
5
6
|
/**
|
|
6
7
|
* Gacha slot machine: reels launch fast, decelerate with momentum, and
|
|
@@ -131,3 +132,145 @@ export function createSlotMachine(options) {
|
|
|
131
132
|
onCleanup(() => cancel?.());
|
|
132
133
|
return { values, result, status, spin, stop, reset };
|
|
133
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Crypto red packet ceremony: tap to open, coins burst out with physics,
|
|
137
|
+
* the amount counts up. The hongbao moment, as a signal-native primitive.
|
|
138
|
+
*
|
|
139
|
+
* The primitive owns the ceremony state machine and the coin particle
|
|
140
|
+
* physics; you render the envelope and the coins however fits your design.
|
|
141
|
+
* Each coin carries position, rotation, size, opacity, and its share of
|
|
142
|
+
* the total amount, split randomly like a real red packet grab.
|
|
143
|
+
*
|
|
144
|
+
* SSR-safe: `open()` jumps straight to revealed on the server. Under
|
|
145
|
+
* reduced motion the packet opens instantly with no burst: the amount
|
|
146
|
+
* just appears.
|
|
147
|
+
*
|
|
148
|
+
* ```tsx
|
|
149
|
+
* import { createRedPacket } from "solid-drift"
|
|
150
|
+
*
|
|
151
|
+
* const packet = createRedPacket({ amount: 88, coins: 14 })
|
|
152
|
+
*
|
|
153
|
+
* <button onClick={() => packet.open()}>
|
|
154
|
+
* {packet.status() === "sealed" ? "🧧 Tap to open" : `$${packet.revealed().toFixed(2)}`}
|
|
155
|
+
* </button>
|
|
156
|
+
* <For each={packet.coins()}>
|
|
157
|
+
* {(coin) => (
|
|
158
|
+
* <div
|
|
159
|
+
* class="coin"
|
|
160
|
+
* style={{
|
|
161
|
+
* transform: `translate(${coin.x}px, ${coin.y}px) rotate(${coin.rotation}deg)`,
|
|
162
|
+
* opacity: coin.opacity,
|
|
163
|
+
* width: `${coin.size}px`,
|
|
164
|
+
* }}
|
|
165
|
+
* />
|
|
166
|
+
* )}
|
|
167
|
+
* </For>
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
export function createRedPacket(options = {}) {
|
|
171
|
+
const { coins: coinCount = 12, amount = 88, spread = 160, gravity = 900, openDuration = 500, burstDuration = 1600, revealDuration = 800, onOpen, onReveal, } = options;
|
|
172
|
+
const [status, setStatus] = createSignal("sealed");
|
|
173
|
+
const [coinList, setCoinList] = createSignal([]);
|
|
174
|
+
const [target, setTarget] = createSignal(0);
|
|
175
|
+
const tweened = createTween(target, {
|
|
176
|
+
duration: revealDuration,
|
|
177
|
+
easing: "easeOutExpo",
|
|
178
|
+
});
|
|
179
|
+
// On the server the tween effect never runs, so read the target directly.
|
|
180
|
+
const revealed = () => typeof window === "undefined" ? target() : tweened();
|
|
181
|
+
let cancel = null;
|
|
182
|
+
let burstAt = 0;
|
|
183
|
+
let lastT = 0;
|
|
184
|
+
let nextCoinId = 1;
|
|
185
|
+
const spawnCoins = () => {
|
|
186
|
+
const weights = Array.from({ length: coinCount }, () => 0.5 + Math.random());
|
|
187
|
+
const totalWeight = weights.reduce((a, b) => a + b, 0);
|
|
188
|
+
const speedScale = spread / 160;
|
|
189
|
+
return weights.map((w, i) => {
|
|
190
|
+
const angle = ((Math.random() * 120 - 60) * Math.PI) / 180;
|
|
191
|
+
const speed = (200 + Math.random() * 220) * speedScale;
|
|
192
|
+
return {
|
|
193
|
+
id: nextCoinId++,
|
|
194
|
+
x: 0,
|
|
195
|
+
y: 0,
|
|
196
|
+
vx: Math.sin(angle) * speed,
|
|
197
|
+
vy: -Math.cos(angle) * speed,
|
|
198
|
+
rotation: Math.random() * 360,
|
|
199
|
+
vr: (Math.random() - 0.5) * 720,
|
|
200
|
+
size: 24 + Math.random() * 16,
|
|
201
|
+
opacity: 1,
|
|
202
|
+
amount: (amount * w) / totalWeight,
|
|
203
|
+
};
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
const stepCoins = (t) => {
|
|
207
|
+
// Every coin is born at burstAt, so they share one age and one fade.
|
|
208
|
+
const dt = Math.min(Math.max((t - lastT) / 1000, 0), 0.05);
|
|
209
|
+
lastT = t;
|
|
210
|
+
const age = t - burstAt;
|
|
211
|
+
if (age >= burstDuration) {
|
|
212
|
+
setCoinList([]);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const fadeStart = burstDuration * 0.7;
|
|
216
|
+
const opacity = age < fadeStart
|
|
217
|
+
? 1
|
|
218
|
+
: Math.max(1 - (age - fadeStart) / (burstDuration - fadeStart), 0);
|
|
219
|
+
setCoinList((prev) => prev.map((c) => {
|
|
220
|
+
const vy = c.vy + gravity * dt;
|
|
221
|
+
return {
|
|
222
|
+
...c,
|
|
223
|
+
x: c.x + c.vx * dt,
|
|
224
|
+
y: c.y + vy * dt,
|
|
225
|
+
vy,
|
|
226
|
+
rotation: c.rotation + c.vr * dt,
|
|
227
|
+
opacity,
|
|
228
|
+
};
|
|
229
|
+
}));
|
|
230
|
+
};
|
|
231
|
+
const open = () => {
|
|
232
|
+
if (status() !== "sealed")
|
|
233
|
+
return;
|
|
234
|
+
onOpen?.();
|
|
235
|
+
if (typeof window === "undefined" || prefersReducedMotion()) {
|
|
236
|
+
// Accessibility / SSR: no ceremony, the amount just appears.
|
|
237
|
+
setTarget(amount);
|
|
238
|
+
setStatus("revealed");
|
|
239
|
+
onReveal?.(amount);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
setStatus("opening");
|
|
243
|
+
const t0 = now();
|
|
244
|
+
cancel?.();
|
|
245
|
+
cancel = schedule((t) => {
|
|
246
|
+
const s = status();
|
|
247
|
+
if (s === "opening" && t - t0 >= openDuration) {
|
|
248
|
+
burstAt = t;
|
|
249
|
+
lastT = t;
|
|
250
|
+
setCoinList(spawnCoins());
|
|
251
|
+
setStatus("bursting");
|
|
252
|
+
}
|
|
253
|
+
else if (s === "bursting") {
|
|
254
|
+
stepCoins(t);
|
|
255
|
+
if (t - burstAt >= burstDuration) {
|
|
256
|
+
setCoinList([]);
|
|
257
|
+
setTarget(amount);
|
|
258
|
+
setStatus("revealed");
|
|
259
|
+
onReveal?.(amount);
|
|
260
|
+
cancel = null;
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return true;
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
const reset = () => {
|
|
268
|
+
cancel?.();
|
|
269
|
+
cancel = null;
|
|
270
|
+
setCoinList([]);
|
|
271
|
+
setTarget(0);
|
|
272
|
+
setStatus("sealed");
|
|
273
|
+
};
|
|
274
|
+
onCleanup(() => cancel?.());
|
|
275
|
+
return { status, coins: coinList, revealed, open, reset };
|
|
276
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export { createScrollProgress, type ScrollTarget } from "./scroll.js";
|
|
|
12
12
|
export { createInView, type InViewOptions } from "./inview.js";
|
|
13
13
|
export { usePrefersReducedMotion, prefersReducedMotion, } from "./reduced-motion.js";
|
|
14
14
|
export { isLowPowerMode, useLowPowerMode, type LowPowerOptions, } from "./power.js";
|
|
15
|
-
export { createSlotMachine, type SlotMachineControls, type SlotMachineOptions, type SlotMachineStatus, } from "./fun.js";
|
|
15
|
+
export { createSlotMachine, type SlotMachineControls, type SlotMachineOptions, type SlotMachineStatus, createRedPacket, type RedPacketCoin, type RedPacketControls, type RedPacketOptions, type RedPacketStatus, } from "./fun.js";
|
|
16
16
|
export { createStagger } from "./stagger.js";
|
|
17
17
|
export { createHorizontalScroll, type HorizontalScrollOptions, type HorizontalScrollResult, } from "./horizontal.js";
|
|
18
18
|
export { createScrub, type ScrubKeyframe, type ScrubOptions } from "./scrub.js";
|
|
@@ -34,3 +34,5 @@ export { createStreamReveal, createAgentState, parseDriftSpec, createSpecPlayer,
|
|
|
34
34
|
export type { StreamRevealStatus, StreamRevealOptions, StreamRevealControls, AgentState, AgentStateTransition, AgentStateOptions, AgentStateControls, DriftSpecPrimitive, DriftSpecStep, DriftSpec, SpecPlayerStatus, SpecPlayerControls, } from "./ai.js";
|
|
35
35
|
export { createTxLifecycle, createTicker, createMintReveal, createConnectButton, } from "./web3.js";
|
|
36
36
|
export type { TxState, TxStatusInput, TxLifecycleOptions, TxLifecycleControls, TickerOptions, TickerControls, MintRevealStatus, MintRevealOptions, MintRevealControls, ConnectButtonOptions, ConnectButtonStatus, ConnectButtonControls, } from "./web3.js";
|
|
37
|
+
export { createPoll, shortenAddress, isAddress, formatUnits, parseUnits, CHAINS, createChain, createTokenPrice, createPriceChange, createPriceCompare, createGasPrice, createBalance, createTxReceipt, createBlockNumber, createChainlinkPrice, createNFTMetadata, createENS, createIdenticon, } from "./web3data.js";
|
|
38
|
+
export type { PollStatus, PollOptions, PollControls, ChainInfo, TokenPrice, TokenPriceOptions, PriceChangeOptions, GasPriceOptions, GasPriceData, BalanceOptions, BalanceData, TxReceiptData, TxReceiptOptions, BlockNumberOptions, ChainlinkPriceOptions, NFTMetadata, NFTMetadataOptions, ENSOptions, IdenticonOptions, } from "./web3data.js";
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ export { createScrollProgress } from "./scroll.js";
|
|
|
12
12
|
export { createInView } from "./inview.js";
|
|
13
13
|
export { usePrefersReducedMotion, prefersReducedMotion, } from "./reduced-motion.js";
|
|
14
14
|
export { isLowPowerMode, useLowPowerMode, } from "./power.js";
|
|
15
|
-
export { createSlotMachine, } from "./fun.js";
|
|
15
|
+
export { createSlotMachine, createRedPacket, } from "./fun.js";
|
|
16
16
|
export { createStagger } from "./stagger.js";
|
|
17
17
|
export { createHorizontalScroll, } from "./horizontal.js";
|
|
18
18
|
export { createScrub } from "./scrub.js";
|
|
@@ -31,3 +31,4 @@ export { createKineticType, createScenePlayer, createShowreel, createCamera, cre
|
|
|
31
31
|
export { createDrag, } from "./gesture.js";
|
|
32
32
|
export { createStreamReveal, createAgentState, parseDriftSpec, createSpecPlayer, DriftSpecError, } from "./ai.js";
|
|
33
33
|
export { createTxLifecycle, createTicker, createMintReveal, createConnectButton, } from "./web3.js";
|
|
34
|
+
export { createPoll, shortenAddress, isAddress, formatUnits, parseUnits, CHAINS, createChain, createTokenPrice, createPriceChange, createPriceCompare, createGasPrice, createBalance, createTxReceipt, createBlockNumber, createChainlinkPrice, createNFTMetadata, createENS, createIdenticon, } from "./web3data.js";
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { type Accessor } from "solid-js";
|
|
2
|
+
/**
|
|
3
|
+
* Web3 data layer: zero-dependency chain and market data as signals.
|
|
4
|
+
*
|
|
5
|
+
* Public RPC and API endpoints over fetch, with user-swappable endpoints.
|
|
6
|
+
* Every network primitive shares the `{ data, error, status, retry, abort }`
|
|
7
|
+
* shape and is SSR-safe (nothing fetches on the server).
|
|
8
|
+
*
|
|
9
|
+
* Honest limits: public endpoints are rate-limited, so default polling
|
|
10
|
+
* intervals are conservative. This is a read-only data layer: transaction
|
|
11
|
+
* signing stays with wallet libraries like wagmi.
|
|
12
|
+
*/
|
|
13
|
+
export type PollStatus = "idle" | "loading" | "success" | "error";
|
|
14
|
+
export interface PollOptions {
|
|
15
|
+
/** Milliseconds between fetches. Default 30000. */
|
|
16
|
+
interval?: number;
|
|
17
|
+
/** Error backoff multiplier. Default 2. */
|
|
18
|
+
backoff?: number;
|
|
19
|
+
/** Backoff cap in milliseconds. Default 300000. */
|
|
20
|
+
maxInterval?: number;
|
|
21
|
+
/** Fetch immediately on creation. Default true. */
|
|
22
|
+
immediate?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface PollControls<T> {
|
|
25
|
+
data: Accessor<T | undefined>;
|
|
26
|
+
error: Accessor<Error | null>;
|
|
27
|
+
status: Accessor<PollStatus>;
|
|
28
|
+
/** Fetch now and reset the backoff. */
|
|
29
|
+
retry: () => void;
|
|
30
|
+
/** Stop polling and abort any in-flight request. */
|
|
31
|
+
abort: () => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Backoff polling infrastructure for the data primitives.
|
|
35
|
+
*
|
|
36
|
+
* Fetches immediately (unless `immediate: false`), then on `interval`.
|
|
37
|
+
* On error the interval multiplies by `backoff` up to `maxInterval` and
|
|
38
|
+
* resets on the next success. Uses `setTimeout`, so background tabs get
|
|
39
|
+
* the browser's natural timer throttling instead of a busy rAF loop.
|
|
40
|
+
* SSR-safe: never fetches on the server.
|
|
41
|
+
*
|
|
42
|
+
* ```ts
|
|
43
|
+
* const { data, status, retry, abort } = createPoll(
|
|
44
|
+
* async (signal) => {
|
|
45
|
+
* const res = await fetch("https://api.example.com/price", { signal });
|
|
46
|
+
* return res.json();
|
|
47
|
+
* },
|
|
48
|
+
* { interval: 30000 },
|
|
49
|
+
* );
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare function createPoll<T>(fetcher: (signal: AbortSignal) => Promise<T>, options?: PollOptions): PollControls<T>;
|
|
53
|
+
/**
|
|
54
|
+
* Shorten an EVM address: `0x1234567890abcdef...` becomes `0x1234…abcd`.
|
|
55
|
+
* Returns the input unchanged when it is not a valid address.
|
|
56
|
+
*/
|
|
57
|
+
export declare function shortenAddress(address: string, chars?: number): string;
|
|
58
|
+
/** True for `0x` + 40 hex chars. Checksum-agnostic. */
|
|
59
|
+
export declare function isAddress(value: string): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Format a wei-style integer as a decimal string: `formatUnits(1500000000000000000n)`
|
|
62
|
+
* is `"1.5"`. Accepts bigint or integer strings. BigInt-safe, no floats.
|
|
63
|
+
*/
|
|
64
|
+
export declare function formatUnits(value: bigint | string, decimals?: number): string;
|
|
65
|
+
/**
|
|
66
|
+
* Parse a decimal string into wei-style bigint: `parseUnits("1.5")` is
|
|
67
|
+
* `1500000000000000000n`. Throws on invalid input or too many decimals.
|
|
68
|
+
*/
|
|
69
|
+
export declare function parseUnits(value: string, decimals?: number): bigint;
|
|
70
|
+
export interface ChainInfo {
|
|
71
|
+
id: number;
|
|
72
|
+
name: string;
|
|
73
|
+
currency: string;
|
|
74
|
+
decimals: number;
|
|
75
|
+
explorer: string;
|
|
76
|
+
rpc: string;
|
|
77
|
+
}
|
|
78
|
+
/** Registry of well-known EVM chains: id to name, currency, explorer, RPC. */
|
|
79
|
+
export declare const CHAINS: Record<number, ChainInfo>;
|
|
80
|
+
/**
|
|
81
|
+
* Look up a chain in `CHAINS` as an accessor. Unknown ids give `undefined`.
|
|
82
|
+
*
|
|
83
|
+
* ```ts
|
|
84
|
+
* const chain = createChain(() => 1)
|
|
85
|
+
* chain()?.explorer // "https://etherscan.io"
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export declare function createChain(source: number | Accessor<number>): Accessor<ChainInfo | undefined>;
|
|
89
|
+
/**
|
|
90
|
+
* keccak256 of a byte array. Exported for tests; not part of the public API.
|
|
91
|
+
*/
|
|
92
|
+
export declare function keccak256(data: Uint8Array): Uint8Array<ArrayBuffer>;
|
|
93
|
+
export interface TokenPrice {
|
|
94
|
+
/** Price in the quote currency. */
|
|
95
|
+
price: number;
|
|
96
|
+
/** 24h change percent, when the source reports it. */
|
|
97
|
+
change24h?: number;
|
|
98
|
+
}
|
|
99
|
+
export interface TokenPriceOptions extends PollOptions {
|
|
100
|
+
/** Quote currency id. Default "usd". */
|
|
101
|
+
vsCurrency?: string;
|
|
102
|
+
/** Price API base. Default CoinGecko public API. */
|
|
103
|
+
endpoint?: string;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Live token price as a signal, via CoinGecko's public API.
|
|
107
|
+
*
|
|
108
|
+
* The free endpoint is rate-limited; the default 60s interval is
|
|
109
|
+
* conservative on purpose. Pass your own `endpoint` (any base that
|
|
110
|
+
* answers `/simple/price?ids={id}&vs_currencies={vs}&include_24hr_change=true`).
|
|
111
|
+
*
|
|
112
|
+
* ```ts
|
|
113
|
+
* const { price, change24h, status } = createTokenPrice("ethereum");
|
|
114
|
+
* <Show when={status() === "success"}>
|
|
115
|
+
* ${(price() ?? 0).toFixed(2)} ({(change24h() ?? 0).toFixed(1)}%)
|
|
116
|
+
* </Show>
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export declare function createTokenPrice(tokenId: string | Accessor<string>, options?: TokenPriceOptions): PollControls<TokenPrice> & {
|
|
120
|
+
price: Accessor<number | undefined>;
|
|
121
|
+
change24h: Accessor<number | undefined>;
|
|
122
|
+
};
|
|
123
|
+
export interface PriceChangeOptions {
|
|
124
|
+
/** Rolling window in ms. Default 3600000 (1h). */
|
|
125
|
+
windowMs?: number;
|
|
126
|
+
/** How often to sample the source in ms. Default 60000. */
|
|
127
|
+
sampleMs?: number;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Percent change of any numeric signal over a rolling window.
|
|
131
|
+
*
|
|
132
|
+
* Samples the source on each change and on `sampleMs`, keeps samples
|
|
133
|
+
* within `windowMs`, and reports `(last - first) / first * 100`.
|
|
134
|
+
* `undefined` until at least two samples exist. Signal-native, no network.
|
|
135
|
+
*
|
|
136
|
+
* ```ts
|
|
137
|
+
* const { price } = createTokenPrice("ethereum");
|
|
138
|
+
* const { change, reset } = createPriceChange(price);
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
export declare function createPriceChange(source: Accessor<number | undefined>, options?: PriceChangeOptions): {
|
|
142
|
+
change: Accessor<number | undefined>;
|
|
143
|
+
reset: () => void;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Compare two numeric signals: their ratio, percent difference, and which
|
|
147
|
+
* is larger. Any side `undefined` makes everything `undefined` until both
|
|
148
|
+
* have values. Signal-native, no network.
|
|
149
|
+
*
|
|
150
|
+
* ```ts
|
|
151
|
+
* const { price: eth } = createTokenPrice("ethereum");
|
|
152
|
+
* const { price: btc } = createTokenPrice("bitcoin");
|
|
153
|
+
* const { ratio, diffPercent, leader } = createPriceCompare(eth, btc);
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export declare function createPriceCompare(a: Accessor<number | undefined>, b: Accessor<number | undefined>): {
|
|
157
|
+
ratio: Accessor<number | undefined>;
|
|
158
|
+
diffPercent: Accessor<number | undefined>;
|
|
159
|
+
leader: Accessor<"a" | "b" | "tie" | undefined>;
|
|
160
|
+
};
|
|
161
|
+
export interface GasPriceOptions extends PollOptions {
|
|
162
|
+
/** JSON-RPC endpoint. Default a public mainnet endpoint. */
|
|
163
|
+
endpoint?: string;
|
|
164
|
+
}
|
|
165
|
+
export interface GasPriceData {
|
|
166
|
+
wei: bigint;
|
|
167
|
+
gwei: number;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Current gas price over JSON-RPC (`eth_gasPrice`), as wei bigint and gwei.
|
|
171
|
+
* Default 15s polling. Swap `endpoint` for any chain.
|
|
172
|
+
*/
|
|
173
|
+
export declare function createGasPrice(options?: GasPriceOptions): PollControls<GasPriceData> & {
|
|
174
|
+
wei: Accessor<bigint | undefined>;
|
|
175
|
+
gwei: Accessor<number | undefined>;
|
|
176
|
+
};
|
|
177
|
+
export interface BalanceOptions extends PollOptions {
|
|
178
|
+
/** JSON-RPC endpoint. Default a public mainnet endpoint. */
|
|
179
|
+
endpoint?: string;
|
|
180
|
+
/** ERC20 token contract. Omit for the native balance. */
|
|
181
|
+
token?: string;
|
|
182
|
+
/** Decimals for formatting. Default 18. */
|
|
183
|
+
decimals?: number;
|
|
184
|
+
}
|
|
185
|
+
export interface BalanceData {
|
|
186
|
+
balance: bigint;
|
|
187
|
+
formatted: string;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Token balance of an address: native (`eth_getBalance`) or ERC20
|
|
191
|
+
* (`balanceOf` via `eth_call`). Read-only; never signs.
|
|
192
|
+
*
|
|
193
|
+
* ```ts
|
|
194
|
+
* const { formatted } = createBalance("0xabc…", { token: "0xdef…" });
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
export declare function createBalance(address: string, options?: BalanceOptions): PollControls<BalanceData> & {
|
|
198
|
+
balance: Accessor<bigint | undefined>;
|
|
199
|
+
formatted: Accessor<string | undefined>;
|
|
200
|
+
};
|
|
201
|
+
export interface TxReceiptData {
|
|
202
|
+
transactionHash: string;
|
|
203
|
+
blockNumber: number;
|
|
204
|
+
/** true when `status` is 0x1, false when 0x0 (reverted). */
|
|
205
|
+
success: boolean;
|
|
206
|
+
gasUsed: bigint;
|
|
207
|
+
}
|
|
208
|
+
export interface TxReceiptOptions extends PollOptions {
|
|
209
|
+
/** JSON-RPC endpoint. Default a public mainnet endpoint. */
|
|
210
|
+
endpoint?: string;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Watch a transaction hash until its receipt lands. Polls every 4s and
|
|
214
|
+
* stops on its own once the receipt arrives; `mined()` mirrors that.
|
|
215
|
+
* Read-only confirmation; pairs with `createTxLifecycle` from web3.ts.
|
|
216
|
+
*/
|
|
217
|
+
export declare function createTxReceipt(hash: string, options?: TxReceiptOptions): PollControls<TxReceiptData | null> & {
|
|
218
|
+
receipt: Accessor<TxReceiptData | null | undefined>;
|
|
219
|
+
mined: Accessor<boolean>;
|
|
220
|
+
};
|
|
221
|
+
export interface BlockNumberOptions extends PollOptions {
|
|
222
|
+
/** JSON-RPC endpoint. Default a public mainnet endpoint. */
|
|
223
|
+
endpoint?: string;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Latest block number over JSON-RPC. Default 12s polling.
|
|
227
|
+
* Handy as a chain-health heartbeat and a cache-busting ticker.
|
|
228
|
+
*/
|
|
229
|
+
export declare function createBlockNumber(options?: BlockNumberOptions): PollControls<number> & {
|
|
230
|
+
blockNumber: Accessor<number | undefined>;
|
|
231
|
+
};
|
|
232
|
+
export interface ChainlinkPriceOptions extends PollOptions {
|
|
233
|
+
/** JSON-RPC endpoint. Default a public mainnet endpoint. */
|
|
234
|
+
endpoint?: string;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Read a Chainlink `AggregatorV3Interface` price feed on-chain:
|
|
238
|
+
* `decimals()` once, then `latestRoundData()` polled every 30s.
|
|
239
|
+
* Feed addresses live on the
|
|
240
|
+
* [Chainlink docs](https://docs.chain.link/data-feeds/price-feeds/addresses).
|
|
241
|
+
*
|
|
242
|
+
* ```ts
|
|
243
|
+
* // ETH / USD feed on mainnet
|
|
244
|
+
* const { price } = createChainlinkPrice("0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419");
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
export declare function createChainlinkPrice(feed: string, options?: ChainlinkPriceOptions): PollControls<number> & {
|
|
248
|
+
price: Accessor<number | undefined>;
|
|
249
|
+
};
|
|
250
|
+
export interface NFTMetadata {
|
|
251
|
+
name?: string;
|
|
252
|
+
description?: string;
|
|
253
|
+
image?: string;
|
|
254
|
+
attributes?: Array<Record<string, unknown>>;
|
|
255
|
+
raw: unknown;
|
|
256
|
+
}
|
|
257
|
+
export interface NFTMetadataOptions {
|
|
258
|
+
/** JSON-RPC endpoint for `tokenURI`. Default a public mainnet endpoint. */
|
|
259
|
+
endpoint?: string;
|
|
260
|
+
/** IPFS gateway base. Default "https://ipfs.io". */
|
|
261
|
+
gateway?: string;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Fetch an NFT's `tokenURI` on-chain and resolve its JSON metadata
|
|
265
|
+
* (one-shot, with `retry`). `ipfs://` URIs are rewritten through the
|
|
266
|
+
* gateway. Returns the parsed fields plus `raw` for anything custom.
|
|
267
|
+
*
|
|
268
|
+
* ```ts
|
|
269
|
+
* const { metadata, image, status, retry } = createNFTMetadata(
|
|
270
|
+
* "0xcontract…",
|
|
271
|
+
* 42,
|
|
272
|
+
* );
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
export declare function createNFTMetadata(contract: string, tokenId: string | number | bigint, options?: NFTMetadataOptions): {
|
|
276
|
+
data: Accessor<NFTMetadata | undefined>;
|
|
277
|
+
error: Accessor<Error | null>;
|
|
278
|
+
status: Accessor<PollStatus>;
|
|
279
|
+
retry: () => void;
|
|
280
|
+
abort: () => void;
|
|
281
|
+
metadata: Accessor<NFTMetadata | undefined>;
|
|
282
|
+
image: Accessor<string | undefined>;
|
|
283
|
+
};
|
|
284
|
+
export interface ENSOptions {
|
|
285
|
+
/** JSON-RPC endpoint. Default a public mainnet endpoint. */
|
|
286
|
+
endpoint?: string;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Reverse-resolve an address to its ENS name via the public ENS registry
|
|
290
|
+
* (one-shot, with `retry`). `undefined` when the address has no name set;
|
|
291
|
+
* errors (network, RPC) surface on `error`.
|
|
292
|
+
*
|
|
293
|
+
* ```ts
|
|
294
|
+
* const { name, status } = createENS("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
export declare function createENS(address: string, options?: ENSOptions): {
|
|
298
|
+
data: Accessor<string | undefined>;
|
|
299
|
+
error: Accessor<Error | null>;
|
|
300
|
+
status: Accessor<PollStatus>;
|
|
301
|
+
retry: () => void;
|
|
302
|
+
abort: () => void;
|
|
303
|
+
name: Accessor<string | undefined>;
|
|
304
|
+
};
|
|
305
|
+
export interface IdenticonOptions {
|
|
306
|
+
/** Pixel size of the square image. Default 64. */
|
|
307
|
+
size?: number;
|
|
308
|
+
/** Grid cells per side. Default 8. */
|
|
309
|
+
cells?: number;
|
|
310
|
+
/** Background color. Default "#f0f0f0". */
|
|
311
|
+
background?: string;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Deterministic identicon avatar for any address as a data URI: a mirrored
|
|
315
|
+
* random-walk grid in SVG, so the same address always renders the same
|
|
316
|
+
* image. Pure computation, works on the server, no network.
|
|
317
|
+
*
|
|
318
|
+
* ```tsx
|
|
319
|
+
* const avatar = createIdenticon("0xabc…");
|
|
320
|
+
* <img src={avatar()} alt="avatar" width={64} height={64} />;
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
export declare function createIdenticon(address: string | Accessor<string>, options?: IdenticonOptions): Accessor<string>;
|