hive-stream 3.0.0 → 3.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/AGENTS.md +35 -0
- package/DOCUMENTATION.md +380 -0
- package/README.md +113 -22
- package/dist/actions.d.ts +3 -3
- package/dist/actions.js +7 -7
- package/dist/actions.js.map +1 -1
- package/dist/adapters/base.adapter.d.ts +19 -1
- package/dist/adapters/base.adapter.js +16 -0
- package/dist/adapters/base.adapter.js.map +1 -1
- package/dist/adapters/mongodb.adapter.d.ts +5 -11
- package/dist/adapters/mongodb.adapter.js +10 -10
- package/dist/adapters/mongodb.adapter.js.map +1 -1
- package/dist/adapters/postgresql.adapter.d.ts +17 -0
- package/dist/adapters/postgresql.adapter.js +99 -8
- package/dist/adapters/postgresql.adapter.js.map +1 -1
- package/dist/adapters/sqlite.adapter.d.ts +17 -0
- package/dist/adapters/sqlite.adapter.js +99 -8
- package/dist/adapters/sqlite.adapter.js.map +1 -1
- package/dist/api.js +86 -0
- package/dist/api.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.js +6 -3
- package/dist/config.js.map +1 -1
- package/dist/contracts/coinflip.contract.d.ts +8 -26
- package/dist/contracts/coinflip.contract.js +123 -144
- package/dist/contracts/coinflip.contract.js.map +1 -1
- package/dist/contracts/contract.d.ts +3 -0
- package/dist/contracts/contract.js +26 -0
- package/dist/contracts/contract.js.map +1 -0
- package/dist/contracts/dice.contract.d.ts +9 -36
- package/dist/contracts/dice.contract.js +135 -200
- package/dist/contracts/dice.contract.js.map +1 -1
- package/dist/contracts/exchange.contract.d.ts +11 -0
- package/dist/contracts/exchange.contract.js +492 -0
- package/dist/contracts/exchange.contract.js.map +1 -0
- package/dist/contracts/lotto.contract.d.ts +15 -19
- package/dist/contracts/lotto.contract.js +154 -162
- package/dist/contracts/lotto.contract.js.map +1 -1
- package/dist/contracts/nft.contract.d.ts +4 -0
- package/dist/contracts/nft.contract.js +65 -0
- package/dist/contracts/nft.contract.js.map +1 -1
- package/dist/contracts/poll.contract.d.ts +4 -0
- package/dist/contracts/poll.contract.js +105 -0
- package/dist/contracts/poll.contract.js.map +1 -0
- package/dist/contracts/rps.contract.d.ts +9 -0
- package/dist/contracts/rps.contract.js +217 -0
- package/dist/contracts/rps.contract.js.map +1 -0
- package/dist/contracts/tipjar.contract.d.ts +4 -0
- package/dist/contracts/tipjar.contract.js +60 -0
- package/dist/contracts/tipjar.contract.js.map +1 -0
- package/dist/contracts/token.contract.d.ts +3 -17
- package/dist/contracts/token.contract.js +128 -80
- package/dist/contracts/token.contract.js.map +1 -1
- package/dist/exchanges/coingecko.d.ts +7 -1
- package/dist/exchanges/coingecko.js +38 -21
- package/dist/exchanges/coingecko.js.map +1 -1
- package/dist/exchanges/exchange.d.ts +15 -8
- package/dist/exchanges/exchange.js +65 -11
- package/dist/exchanges/exchange.js.map +1 -1
- package/dist/hive-rates.d.ts +29 -4
- package/dist/hive-rates.js +179 -92
- package/dist/hive-rates.js.map +1 -1
- package/dist/index.d.ts +10 -3
- package/dist/index.js +18 -4
- package/dist/index.js.map +1 -1
- package/dist/streamer.d.ts +101 -8
- package/dist/streamer.js +410 -140
- package/dist/streamer.js.map +1 -1
- package/dist/test.js +11 -12
- package/dist/test.js.map +1 -1
- package/dist/types/hive-stream.d.ts +85 -14
- package/dist/types/rates.d.ts +47 -0
- package/dist/types/rates.js +29 -0
- package/dist/types/rates.js.map +1 -0
- package/dist/utils.d.ts +318 -11
- package/dist/utils.js +804 -115
- package/dist/utils.js.map +1 -1
- package/examples/contracts/README.md +8 -0
- package/examples/contracts/exchange.ts +38 -0
- package/examples/contracts/poll.ts +21 -0
- package/examples/contracts/rps.ts +19 -0
- package/examples/contracts/tipjar.ts +19 -0
- package/package.json +20 -19
- package/tests/actions.spec.ts +7 -7
- package/tests/adapters/actions-persistence.spec.ts +4 -4
- package/tests/adapters/sqlite.adapter.spec.ts +2 -2
- package/tests/contracts/coinflip.contract.spec.ts +26 -154
- package/tests/contracts/dice.contract.spec.ts +24 -140
- package/tests/contracts/exchange.contract.spec.ts +84 -0
- package/tests/contracts/lotto.contract.spec.ts +30 -295
- package/tests/contracts/token.contract.spec.ts +72 -316
- package/tests/exchanges/coingecko.exchange.spec.ts +169 -0
- package/tests/exchanges/exchange.base.spec.ts +246 -0
- package/tests/helpers/mock-fetch.ts +165 -0
- package/tests/hive-chain-features.spec.ts +238 -0
- package/tests/hive-rates.spec.ts +443 -0
- package/tests/integration/hive-rates.integration.spec.ts +35 -0
- package/tests/streamer-actions.spec.ts +29 -18
- package/tests/streamer.spec.ts +142 -49
- package/tests/types/rates.spec.ts +216 -0
- package/tests/utils.spec.ts +27 -6
package/AGENTS.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Repository Guidelines
|
|
2
|
+
|
|
3
|
+
## Project Structure & Module Organization
|
|
4
|
+
- `src/` holds TypeScript source. Core entry points are `streamer.ts`, `api.ts`, and `index.ts`.
|
|
5
|
+
- `src/adapters/` contains SQLite/Mongo/Postgres adapters; `src/contracts/` has contract examples; `src/exchanges/` has rate helpers; `src/types/` centralizes shared types.
|
|
6
|
+
- `tests/` contains Jest specs (`**/*.spec.ts`) plus `tests/integration/` and `tests/helpers/`.
|
|
7
|
+
- `dist/` is generated build output; `examples/` provides usage samples; `ecosystem.config.js` is a PM2 reference.
|
|
8
|
+
|
|
9
|
+
## Build, Test, and Development Commands
|
|
10
|
+
- `npm install` installs dependencies.
|
|
11
|
+
- `npm run build` compiles TypeScript to `dist/`.
|
|
12
|
+
- `npm run start` runs the local dev harness (`src/test.ts`) via ts-node.
|
|
13
|
+
- `npm run watch` enables TypeScript watch mode.
|
|
14
|
+
- `npm test` runs the Jest suite.
|
|
15
|
+
- `npm run clean-tests` clears Jest cache if tests act stale.
|
|
16
|
+
|
|
17
|
+
## Coding Style & Naming Conventions
|
|
18
|
+
- TypeScript with CommonJS output; keep module boundaries within `src/`.
|
|
19
|
+
- Indent 4 spaces; prefer single quotes (see `tslint.json`).
|
|
20
|
+
- File naming conventions: `*.adapter.ts`, `*.contract.ts`, `*.spec.ts`; shared types live in `src/types/`.
|
|
21
|
+
- TSLint rules in `tslint.json` are the baseline; match existing style before introducing new tooling.
|
|
22
|
+
|
|
23
|
+
## Testing Guidelines
|
|
24
|
+
- Framework: Jest + `ts-jest` (see `jest.config.js`).
|
|
25
|
+
- Place specs under `tests/` and name `*.spec.ts` (example: `tests/utils.spec.ts`).
|
|
26
|
+
- Use `tests/setup.ts` for shared mocks; integration tests live under `tests/integration/`.
|
|
27
|
+
- No coverage thresholds are configured; add focused tests for new adapters, contracts, or API routes.
|
|
28
|
+
|
|
29
|
+
## Commit & Pull Request Guidelines
|
|
30
|
+
- Follow Conventional Commits with scopes, as in history: `feat(api): ...`, `fix(contract): ...`, `chore(deps): ...`, `docs(readme): ...`.
|
|
31
|
+
- PRs should include: a clear summary, testing notes (`npm test`/`npm run build`), and docs/CHANGELOG updates for user-facing changes.
|
|
32
|
+
|
|
33
|
+
## Configuration & Security Notes
|
|
34
|
+
- Keys and usernames should come from env/config (see `src/config.ts`); never commit secrets.
|
|
35
|
+
- When adding new config, document defaults and keep backward compatibility.
|
package/DOCUMENTATION.md
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# Hive-Stream Documentation
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
Hive Stream is a Node.js library for streaming Hive blockchain activity and routing it to contracts you define. Contracts can react to:
|
|
5
|
+
- `custom_json` operations
|
|
6
|
+
- transfer memos
|
|
7
|
+
- time-based actions
|
|
8
|
+
- escrow operations
|
|
9
|
+
|
|
10
|
+
This document focuses on the contract system and how to build robust contracts using the new `defineContract`/`action` API.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Concepts
|
|
15
|
+
|
|
16
|
+
### Contracts
|
|
17
|
+
A **contract** is a definition object with:
|
|
18
|
+
- a **name**
|
|
19
|
+
- one or more **actions**
|
|
20
|
+
- optional **lifecycle hooks**
|
|
21
|
+
|
|
22
|
+
Contracts are registered with the `Streamer` and called when a payload matches `contract` + `action`.
|
|
23
|
+
|
|
24
|
+
### Actions
|
|
25
|
+
An **action** is a handler function plus metadata such as:
|
|
26
|
+
- the trigger (`custom_json`, `transfer`, or `time`)
|
|
27
|
+
- the trigger (`custom_json`, `transfer`, `time`, `escrow_transfer`, `escrow_approve`, `escrow_dispute`, `escrow_release`, or `recurrent_transfer`)
|
|
28
|
+
- an optional Zod schema to validate payloads
|
|
29
|
+
- whether it requires an active key signature
|
|
30
|
+
|
|
31
|
+
### Payload Identifier
|
|
32
|
+
Hive Stream extracts payloads from:
|
|
33
|
+
- **custom_json**: `op[1].json`
|
|
34
|
+
- **transfer memo**: `op[1].memo`
|
|
35
|
+
|
|
36
|
+
It expects a wrapper object that looks like:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
{
|
|
40
|
+
"hive_stream": {
|
|
41
|
+
"contract": "mycontract",
|
|
42
|
+
"action": "doSomething",
|
|
43
|
+
"payload": { "any": "data" },
|
|
44
|
+
"meta": { "optional": true }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The wrapper key `hive_stream` is the default `PAYLOAD_IDENTIFIER`. You can change it in config.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Contract API
|
|
54
|
+
|
|
55
|
+
### Define a Contract
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { defineContract, action } from 'hive-stream';
|
|
59
|
+
|
|
60
|
+
const MyContract = defineContract({
|
|
61
|
+
name: 'mycontract',
|
|
62
|
+
actions: {
|
|
63
|
+
hello: action(async (payload, ctx) => {
|
|
64
|
+
console.log('hello', payload.name, ctx.sender);
|
|
65
|
+
}, {
|
|
66
|
+
trigger: 'custom_json'
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Register a Contract
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { Streamer } from 'hive-stream';
|
|
76
|
+
|
|
77
|
+
const streamer = new Streamer();
|
|
78
|
+
await streamer.registerContract(MyContract);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Unregister a Contract
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
await streamer.unregisterContract('mycontract');
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Contract Lifecycle Hooks
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
const MyContract = defineContract({
|
|
91
|
+
name: 'mycontract',
|
|
92
|
+
hooks: {
|
|
93
|
+
create: async ({ streamer, adapter, config }) => {
|
|
94
|
+
// initialize tables, cache config, etc
|
|
95
|
+
},
|
|
96
|
+
destroy: async ({ streamer, adapter }) => {
|
|
97
|
+
// cleanup resources
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
actions: { ... }
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Contract Context
|
|
107
|
+
|
|
108
|
+
Every action receives a `ContractContext` with:
|
|
109
|
+
|
|
110
|
+
- `trigger`: `custom_json | transfer | time | escrow_transfer | escrow_approve | escrow_dispute | escrow_release | recurrent_transfer`
|
|
111
|
+
- `streamer`: access to Hive client and helpers
|
|
112
|
+
- `adapter`: database adapter
|
|
113
|
+
- `config`: resolved config values
|
|
114
|
+
- `block`: `{ number, id, previousId, time }`
|
|
115
|
+
- `transaction`: `{ id }`
|
|
116
|
+
- `sender`: Hive account invoking the action
|
|
117
|
+
- `transfer`: transfer details (only for `transfer` trigger)
|
|
118
|
+
- `customJson`: custom JSON details (only for `custom_json` trigger)
|
|
119
|
+
- `escrow`: escrow details (only for escrow triggers)
|
|
120
|
+
- `operation`: raw operation data and operation type for advanced use cases
|
|
121
|
+
|
|
122
|
+
### Example
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
action((payload, ctx) => {
|
|
126
|
+
console.log(ctx.trigger);
|
|
127
|
+
console.log(ctx.block.number, ctx.transaction.id);
|
|
128
|
+
if (ctx.transfer) {
|
|
129
|
+
console.log(ctx.transfer.amount, ctx.transfer.asset);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Payload Validation (Zod)
|
|
137
|
+
|
|
138
|
+
Use Zod to validate payloads and enforce strong inputs:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import { z } from 'zod';
|
|
142
|
+
|
|
143
|
+
const schema = z.object({
|
|
144
|
+
amount: z.string().min(1),
|
|
145
|
+
to: z.string().min(1)
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const MyContract = defineContract({
|
|
149
|
+
name: 'payments',
|
|
150
|
+
actions: {
|
|
151
|
+
send: action((payload, ctx) => {
|
|
152
|
+
// payload is validated here
|
|
153
|
+
}, { schema, trigger: 'custom_json' })
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
If validation fails, the action is not executed and the error is logged.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Triggers
|
|
163
|
+
|
|
164
|
+
Actions can be scoped to a trigger:
|
|
165
|
+
|
|
166
|
+
- `custom_json`: called when a custom JSON payload matches
|
|
167
|
+
- `transfer`: called when a transfer memo matches
|
|
168
|
+
- `time`: called by a `TimeAction`
|
|
169
|
+
- `escrow_transfer`: called when escrow transfer payload in `json_meta` matches
|
|
170
|
+
- `escrow_approve`: available as a trigger for escrow-related contracts
|
|
171
|
+
- `escrow_dispute`: available as a trigger for escrow-related contracts
|
|
172
|
+
- `escrow_release`: available as a trigger for escrow-related contracts
|
|
173
|
+
- `recurrent_transfer`: called when recurrent transfer memo payload matches
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
action(handler, { trigger: 'transfer' });
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
You can also allow multiple triggers:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
action(handler, { trigger: ['custom_json', 'transfer'] });
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Active Key Enforcement
|
|
188
|
+
|
|
189
|
+
Some actions should only run when the transaction is signed with an active key. Mark them with:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
action(handler, { trigger: 'custom_json', requiresActiveKey: true });
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
This prevents posting-key JSONs from triggering sensitive actions like withdrawals.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Error Handling
|
|
200
|
+
|
|
201
|
+
- Errors inside contract actions are caught and logged.
|
|
202
|
+
- **Time actions** bubble errors back into the scheduler so the action does **not** increment execution count if it fails.
|
|
203
|
+
|
|
204
|
+
Write actions defensively and always validate inputs.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Time-Based Actions
|
|
209
|
+
|
|
210
|
+
Time-based actions let you run contract logic on a schedule:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
import { TimeAction } from 'hive-stream';
|
|
214
|
+
|
|
215
|
+
const matchAction = new TimeAction('30s', 'exchange-matcher', 'exchange', 'matchOrders', { limit: 50 });
|
|
216
|
+
await streamer.registerAction(matchAction);
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The action must have `trigger: 'time'` in its definition.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Adapter Requirements
|
|
224
|
+
|
|
225
|
+
Contracts can use any adapter, but some require SQL features:
|
|
226
|
+
|
|
227
|
+
- **SQL adapters** (SQLite/Postgres) support `adapter.query(...)`.
|
|
228
|
+
- **MongoDB adapter** does not support raw SQL.
|
|
229
|
+
|
|
230
|
+
If a contract uses `adapter.query`, it must document that it requires a SQL adapter.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Building Contracts Step-by-Step
|
|
235
|
+
|
|
236
|
+
1. **Define the contract** with `defineContract`.
|
|
237
|
+
2. **Define actions** with `action`, specifying trigger and optional schema.
|
|
238
|
+
3. **Use hooks** (`create`, `destroy`) to initialize tables or cache configuration.
|
|
239
|
+
4. **Validate input** using Zod.
|
|
240
|
+
5. **Use `ctx`** for block/transaction context.
|
|
241
|
+
6. **Return void** (actions are fire-and-forget).
|
|
242
|
+
7. **Register the contract** with the streamer.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Example Contract
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
import { defineContract, action } from 'hive-stream';
|
|
250
|
+
import { z } from 'zod';
|
|
251
|
+
|
|
252
|
+
const tipSchema = z.object({
|
|
253
|
+
message: z.string().max(280).optional()
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
export const TipJar = defineContract({
|
|
257
|
+
name: 'tipjar',
|
|
258
|
+
actions: {
|
|
259
|
+
tip: action(async (payload, ctx) => {
|
|
260
|
+
if (!ctx.transfer) {
|
|
261
|
+
throw new Error('Transfer context required');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
console.log(`Tip from ${ctx.sender}: ${ctx.transfer.amount} ${ctx.transfer.asset}`);
|
|
265
|
+
if (payload.message) {
|
|
266
|
+
console.log(`Message: ${payload.message}`);
|
|
267
|
+
}
|
|
268
|
+
}, {
|
|
269
|
+
schema: tipSchema,
|
|
270
|
+
trigger: 'transfer'
|
|
271
|
+
})
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Built-in Contract Examples
|
|
279
|
+
|
|
280
|
+
- `createDiceContract` - Dice game using transfer bets
|
|
281
|
+
- `createCoinflipContract` - Coin flip game using transfer bets
|
|
282
|
+
- `createLottoContract` - Lottery system with scheduled draws
|
|
283
|
+
- `createTokenContract` - SQL-backed fungible tokens
|
|
284
|
+
- `createNFTContract` - SQL-backed NFTs
|
|
285
|
+
- `createRpsContract` - Rock/Paper/Scissors
|
|
286
|
+
- `createPollContract` - Polls and votes
|
|
287
|
+
- `createTipJarContract` - Tip jar + message log
|
|
288
|
+
- `createExchangeContract` - Deposits/withdrawals/orders/matching (SQL)
|
|
289
|
+
|
|
290
|
+
### Example Snippets
|
|
291
|
+
|
|
292
|
+
Quick-start snippets live in `examples/contracts/`:
|
|
293
|
+
|
|
294
|
+
- `examples/contracts/rps.ts`
|
|
295
|
+
- `examples/contracts/poll.ts`
|
|
296
|
+
- `examples/contracts/tipjar.ts`
|
|
297
|
+
- `examples/contracts/exchange.ts`
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Exchange Contract Guide
|
|
302
|
+
|
|
303
|
+
The exchange contract gives you a basic on-chain orderbook experience backed by a SQL adapter.
|
|
304
|
+
|
|
305
|
+
### Features
|
|
306
|
+
- Deposits via transfer memo
|
|
307
|
+
- Internal balances (available + locked)
|
|
308
|
+
- Order placement and cancellation
|
|
309
|
+
- Order matching
|
|
310
|
+
- Withdrawals (active key required)
|
|
311
|
+
- Internal transfers between exchange users
|
|
312
|
+
- Maker/taker fees (basis points)
|
|
313
|
+
- Order book snapshots for API consumption
|
|
314
|
+
|
|
315
|
+
### Notes
|
|
316
|
+
- Requires a SQL adapter (SQLite or Postgres).
|
|
317
|
+
- Uses `TimeAction` to run `matchOrders` periodically.
|
|
318
|
+
- Fees are charged on received assets (base for buyers, quote for sellers).
|
|
319
|
+
|
|
320
|
+
### Configuration Options
|
|
321
|
+
```
|
|
322
|
+
createExchangeContract({
|
|
323
|
+
name: 'exchange',
|
|
324
|
+
account: 'my-exchange',
|
|
325
|
+
feeAccount: 'my-exchange-fees',
|
|
326
|
+
makerFeeBps: 10,
|
|
327
|
+
takerFeeBps: 20
|
|
328
|
+
})
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Example Payloads
|
|
332
|
+
|
|
333
|
+
**Create pair**
|
|
334
|
+
```
|
|
335
|
+
{"hive_stream": {"contract":"exchange","action":"createPair","payload":{"base":"HIVE","quote":"HBD"}}}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Deposit** (send transfer to exchange account with memo)
|
|
339
|
+
```
|
|
340
|
+
{"hive_stream": {"contract":"exchange","action":"deposit","payload":{}}}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**Place order**
|
|
344
|
+
```
|
|
345
|
+
{"hive_stream": {"contract":"exchange","action":"placeOrder","payload":{"side":"buy","base":"HIVE","quote":"HBD","price":"2","amount":"5"}}}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
**Cancel order**
|
|
349
|
+
```
|
|
350
|
+
{"hive_stream": {"contract":"exchange","action":"cancelOrder","payload":{"orderId":"..."}}}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**Snapshot orderbook**
|
|
354
|
+
```
|
|
355
|
+
{"hive_stream": {"contract":"exchange","action":"snapshotOrderBook","payload":{"base":"HIVE","quote":"HBD","depth":20}}}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**Withdraw**
|
|
359
|
+
```
|
|
360
|
+
{"hive_stream": {"contract":"exchange","action":"withdraw","payload":{"asset":"HBD","amount":"5.000"}}}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### API Endpoints
|
|
364
|
+
|
|
365
|
+
If you run the built-in API server, the following endpoints are available:
|
|
366
|
+
|
|
367
|
+
- `GET /exchange/balances` (optional query `?account=alice`)
|
|
368
|
+
- `GET /exchange/orders` (query `account`, `base`, `quote`, `status`)
|
|
369
|
+
- `GET /exchange/trades` (query `account`, `base`, `quote`)
|
|
370
|
+
- `GET /exchange/orderbook` (query `base`, `quote`, `limit`)
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## Utilities
|
|
375
|
+
The library includes helpers for JSON parsing, randomness, and transfer verification. See `src/utils.ts` for details.
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Tests
|
|
380
|
+
Contract tests live under `tests/contracts/`. Time action tests are in `tests/streamer-actions.spec.ts`.
|
package/README.md
CHANGED
|
@@ -29,6 +29,10 @@ The `BLOCK_CHECK_INTERVAL` value is how often to check for new blocks or in case
|
|
|
29
29
|
|
|
30
30
|
The `BLOCKS_BEHIND_WARNING` value is a numeric value of the number of blocks your API will fall behind from the master before warning to the console.
|
|
31
31
|
|
|
32
|
+
To resume automatically from stored state, keep `RESUME_FROM_STATE` enabled (default). To force a specific start block, set `RESUME_FROM_STATE` to `false` and supply `LAST_BLOCK_NUMBER`.
|
|
33
|
+
|
|
34
|
+
For faster catch-up, `CATCH_UP_BATCH_SIZE` controls how many blocks are processed per polling cycle, and `CATCH_UP_DELAY_MS` controls the delay between catch-up batches (set to `0` for fastest catch-up).
|
|
35
|
+
|
|
32
36
|
The `API_NODES` are the Hive API endpoints used for failover. If you want to enable debug mode, set `DEBUG_MODE` to `true`. The configuration values and their defaults can be found in `src/config.ts`.
|
|
33
37
|
|
|
34
38
|
```
|
|
@@ -40,6 +44,9 @@ const options = {
|
|
|
40
44
|
LAST_BLOCK_NUMBER: 0,
|
|
41
45
|
BLOCK_CHECK_INTERVAL: 1000,
|
|
42
46
|
BLOCKS_BEHIND_WARNING: 25,
|
|
47
|
+
RESUME_FROM_STATE: true,
|
|
48
|
+
CATCH_UP_BATCH_SIZE: 50,
|
|
49
|
+
CATCH_UP_DELAY_MS: 0,
|
|
43
50
|
API_NODES: ['https://api.hive.blog', 'https://api.openhive.network', 'https://rpc.ausbit.dev'],
|
|
44
51
|
DEBUG_MODE: false
|
|
45
52
|
}
|
|
@@ -70,6 +77,25 @@ ss.onTransfer((op, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
|
|
|
70
77
|
})
|
|
71
78
|
```
|
|
72
79
|
|
|
80
|
+
#### Watch for escrow operations
|
|
81
|
+
```javascript
|
|
82
|
+
ss.onEscrowTransfer((op, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
|
|
83
|
+
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
ss.onEscrowApprove((op, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
|
|
87
|
+
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
ss.onEscrowDispute((op, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
|
|
91
|
+
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
ss.onEscrowRelease((op, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
|
|
95
|
+
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
73
99
|
#### Watch for custom JSON operations
|
|
74
100
|
```javascript
|
|
75
101
|
ss.onCustomJson((op, { sender, isSignedWithActiveKey }, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
|
|
@@ -145,6 +171,42 @@ issueHiveEngineTokensMultiple(from, accounts = [], symbol, memo = '', amount = '
|
|
|
145
171
|
}
|
|
146
172
|
```
|
|
147
173
|
|
|
174
|
+
### Escrow Operations
|
|
175
|
+
```javascript
|
|
176
|
+
escrowTransfer({
|
|
177
|
+
from,
|
|
178
|
+
to,
|
|
179
|
+
agent,
|
|
180
|
+
escrow_id,
|
|
181
|
+
hive_amount = '0.000 HIVE',
|
|
182
|
+
hbd_amount = '0.000 HBD',
|
|
183
|
+
fee,
|
|
184
|
+
ratification_deadline,
|
|
185
|
+
escrow_expiration,
|
|
186
|
+
json_meta
|
|
187
|
+
}, signingKeys?)
|
|
188
|
+
|
|
189
|
+
escrowApprove({ from, to, agent, who, escrow_id, approve }, signingKeys?)
|
|
190
|
+
escrowDispute({ from, to, agent, who, escrow_id }, signingKeys?)
|
|
191
|
+
escrowRelease({ from, to, agent, who, receiver, escrow_id, hive_amount, hbd_amount }, signingKeys?)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Multisig + Authority Helpers
|
|
195
|
+
```javascript
|
|
196
|
+
broadcastOperations(operations, signingKeys?)
|
|
197
|
+
broadcastMultiSigOperations(operations, signingKeys)
|
|
198
|
+
createAuthority(keyAuths, accountAuths, weightThreshold)
|
|
199
|
+
updateAccountAuthorities(account, authorityUpdate, signingKeys?)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Recurrent Transfers + Governance
|
|
203
|
+
```javascript
|
|
204
|
+
recurrentTransfer({ from, to, amount, memo, recurrence, executions }, signingKeys?)
|
|
205
|
+
createProposal({ creator, receiver, start_date, end_date, daily_pay, subject, permlink }, signingKeys?)
|
|
206
|
+
updateProposalVotes({ voter, proposal_ids, approve }, signingKeys?)
|
|
207
|
+
removeProposals({ proposal_owner, proposal_ids }, signingKeys?)
|
|
208
|
+
```
|
|
209
|
+
|
|
148
210
|
### Upvote/Downvote Posts
|
|
149
211
|
```javascript
|
|
150
212
|
upvote(votePercentage = '100.0', username, permlink) {
|
|
@@ -158,28 +220,38 @@ downvote(votePercentage = '100.0', username, permlink) {
|
|
|
158
220
|
|
|
159
221
|
## Contracts
|
|
160
222
|
|
|
161
|
-
Hive Stream allows you to
|
|
162
|
-
|
|
163
|
-
The payload consists of:
|
|
164
|
-
|
|
165
|
-
`name` the name of the smart contract you registered.
|
|
223
|
+
Hive Stream allows you to register contract definitions that execute when a transfer memo or custom JSON operation matches. The payload lives under the `PAYLOAD_IDENTIFIER` (default: `hive_stream`).
|
|
166
224
|
|
|
167
|
-
|
|
225
|
+
The payload shape is:
|
|
168
226
|
|
|
169
|
-
`
|
|
227
|
+
- `contract`: the name of the contract you registered
|
|
228
|
+
- `action`: the action name defined in your contract
|
|
229
|
+
- `payload`: data passed to the action
|
|
230
|
+
- `meta`: optional metadata
|
|
170
231
|
|
|
171
232
|
### Writing contracts
|
|
172
233
|
|
|
173
|
-
|
|
234
|
+
Contracts are defined with `defineContract` + `action`. Each action can specify a trigger (`custom_json`, `transfer`, `time`, `escrow_transfer`, `escrow_approve`, `escrow_dispute`, `escrow_release`, or `recurrent_transfer`) and an optional Zod schema for payload validation.
|
|
235
|
+
|
|
236
|
+
For a full contract-building guide (payloads, context, triggers, validation, error handling, and exchange setup), see `DOCUMENTATION.md`.
|
|
174
237
|
|
|
175
238
|
### Register a contract
|
|
176
239
|
|
|
177
|
-
Register a
|
|
240
|
+
Register a contract definition. Registration is async so hooks can initialize state.
|
|
178
241
|
|
|
179
242
|
```javascript
|
|
180
|
-
import
|
|
243
|
+
import { defineContract, action } from 'hive-stream';
|
|
244
|
+
|
|
245
|
+
const MyContract = defineContract({
|
|
246
|
+
name: 'mycontract',
|
|
247
|
+
actions: {
|
|
248
|
+
hello: action(async (payload, ctx) => {
|
|
249
|
+
console.log('hello', payload, ctx.sender);
|
|
250
|
+
}, { trigger: 'custom_json' })
|
|
251
|
+
}
|
|
252
|
+
});
|
|
181
253
|
|
|
182
|
-
registerContract(
|
|
254
|
+
await streamer.registerContract(MyContract);
|
|
183
255
|
```
|
|
184
256
|
|
|
185
257
|
### Unregister a contract
|
|
@@ -187,33 +259,52 @@ registerContract('mycontract', Contract);
|
|
|
187
259
|
Unregister a contract that has been registered.
|
|
188
260
|
|
|
189
261
|
```javascript
|
|
190
|
-
unregisterContract('mycontract');
|
|
262
|
+
await streamer.unregisterContract('mycontract');
|
|
191
263
|
```
|
|
192
264
|
|
|
193
265
|
### Example Payload
|
|
194
266
|
|
|
195
267
|
```javascript
|
|
196
|
-
JSON.stringify({
|
|
268
|
+
JSON.stringify({
|
|
269
|
+
hive_stream: {
|
|
270
|
+
contract: 'hivedice',
|
|
271
|
+
action: 'roll',
|
|
272
|
+
payload: { roll: 22 }
|
|
273
|
+
}
|
|
274
|
+
})
|
|
197
275
|
```
|
|
198
276
|
|
|
199
|
-
This will match a registered contract called `hivedice
|
|
277
|
+
This will match a registered contract called `hivedice`, run the `roll` action, and pass the payload into your handler.
|
|
200
278
|
|
|
201
279
|
### Built-in Contract Examples
|
|
202
280
|
|
|
203
281
|
The library includes several built-in contract examples in the `src/contracts` folder:
|
|
204
282
|
|
|
205
|
-
- `
|
|
206
|
-
- `
|
|
207
|
-
- `
|
|
208
|
-
- `
|
|
209
|
-
- `
|
|
283
|
+
- `createDiceContract` - A dice rolling game contract
|
|
284
|
+
- `createCoinflipContract` - A coin flip game contract
|
|
285
|
+
- `createLottoContract` - A lottery-style game contract
|
|
286
|
+
- `createTokenContract` - A contract for token operations
|
|
287
|
+
- `createNFTContract` - A contract for NFT operations
|
|
288
|
+
- `createRpsContract` - A rock-paper-scissors game contract
|
|
289
|
+
- `createPollContract` - A poll/voting contract
|
|
290
|
+
- `createTipJarContract` - A tip jar + message board contract
|
|
291
|
+
- `createExchangeContract` - A basic exchange with deposits, withdrawals, balances, and order matching (SQL adapter required)
|
|
210
292
|
|
|
211
293
|
These can be imported and used as examples for building your own contracts:
|
|
212
294
|
|
|
213
295
|
```javascript
|
|
214
|
-
import {
|
|
296
|
+
import { createDiceContract, createCoinflipContract, createLottoContract } from 'hive-stream';
|
|
215
297
|
```
|
|
216
298
|
|
|
299
|
+
### Example Snippets
|
|
300
|
+
|
|
301
|
+
Sample snippets for the newest contracts live in `examples/contracts/`:
|
|
302
|
+
|
|
303
|
+
- `examples/contracts/rps.ts`
|
|
304
|
+
- `examples/contracts/poll.ts`
|
|
305
|
+
- `examples/contracts/tipjar.ts`
|
|
306
|
+
- `examples/contracts/exchange.ts`
|
|
307
|
+
|
|
217
308
|
## Time-based Actions
|
|
218
309
|
|
|
219
310
|
It's like a cron job for your contracts. Time-based actions allow you to execute contract functions over a wide variety of different periods. Want to call a function every 3 seconds block time or want to call a function once per day? Time-based actions are an easy way to run time code.
|
|
@@ -239,11 +330,11 @@ The `TimeAction` instance accepts the following values:
|
|
|
239
330
|
- timeValue - When should this action be run?
|
|
240
331
|
- uniqueId - A unique ID to describe your action
|
|
241
332
|
- contractName - The name of the contract
|
|
242
|
-
-
|
|
333
|
+
- contractAction - The action we are calling inside of the contract
|
|
243
334
|
- date - An optional final parameter that accepts a date of creation
|
|
244
335
|
|
|
245
336
|
```
|
|
246
|
-
new TimeAction(timeValue, uniqueId, contractName,
|
|
337
|
+
new TimeAction(timeValue, uniqueId, contractName, contractAction, date)
|
|
247
338
|
```
|
|
248
339
|
|
|
249
340
|
### Valid time values
|
package/dist/actions.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export interface TimeActionInterface {
|
|
|
2
2
|
timeValue: string;
|
|
3
3
|
id: string;
|
|
4
4
|
contractName: string;
|
|
5
|
-
|
|
5
|
+
contractAction: string;
|
|
6
6
|
payload: any;
|
|
7
7
|
date: Date;
|
|
8
8
|
enabled: boolean;
|
|
@@ -15,7 +15,7 @@ export declare class TimeAction implements TimeActionInterface {
|
|
|
15
15
|
timeValue: string;
|
|
16
16
|
id: string;
|
|
17
17
|
contractName: string;
|
|
18
|
-
|
|
18
|
+
contractAction: string;
|
|
19
19
|
payload: any;
|
|
20
20
|
date: Date;
|
|
21
21
|
enabled: boolean;
|
|
@@ -24,7 +24,7 @@ export declare class TimeAction implements TimeActionInterface {
|
|
|
24
24
|
maxExecutions?: number;
|
|
25
25
|
timezone?: string;
|
|
26
26
|
private static readonly VALID_TIME_VALUES;
|
|
27
|
-
constructor(timeValue: string, id: string, contractName: string,
|
|
27
|
+
constructor(timeValue: string, id: string, contractName: string, contractAction: string, payload?: any, date?: Date | string, enabled?: boolean, executionCount?: number, maxExecutions?: number, timezone?: string);
|
|
28
28
|
private validateTimeValue;
|
|
29
29
|
private validateId;
|
|
30
30
|
private validateContractName;
|