@ixo/oracle-runtime-workers 0.31.0 → 0.32.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/package.json +4 -4
- package/src/core/middlewares/tool-repetition-guard.ts +6 -3
- package/src/core/middlewares/turn-boundary.ts +6 -1
- package/src/core/tool-not-run.ts +30 -0
- package/src/core/wrap-plugin-tool.ts +9 -1
- package/src/do/user-oracle-do.ts +78 -15
- package/src/hub-actions/chain-tx-refusals.ts +217 -0
- package/src/hub-actions/client.ts +91 -27
- package/src/hub-actions/request.ts +32 -25
- package/src/hub-actions/transfer.ts +76 -26
- package/src/plugin-api/types.ts +59 -18
- package/src/plugins/hub-actions/chain-tx-catalog.ts +1239 -0
- package/src/plugins/hub-actions/hub-actions.plugin.ts +394 -215
- package/src/plugins/ixo-chain/ixo-chain.plugin.ts +1 -1
- package/src/plugins/ixo-chain/kinds/chain/index.ts +2 -0
- package/src/plugins/ixo-chain/kinds/chain/validators.ts +151 -0
- package/src/plugins/yoma/engine.ts +107 -55
- package/src/plugins/yoma/submission.ts +3 -6
- package/src/plugins/yoma/yoma.plugin.ts +12 -3
|
@@ -86,8 +86,13 @@ export interface HubActionTransport {
|
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
88
|
* The protocol version this runtime declares on every operation call
|
|
89
|
-
* (capabilities, POST operations, GET status, options). Version
|
|
90
|
-
* 0.
|
|
89
|
+
* (capabilities, POST operations, GET status, options). Version 6 is the
|
|
90
|
+
* 0.8.0 contract: version 5 plus 29 more `ixo.chain.tx.v1` message types
|
|
91
|
+
* (staking, voting, retiring impact tokens, claim collections, entities and
|
|
92
|
+
* identity-record links), which a 0.7.0 parser cannot read in capabilities,
|
|
93
|
+
* review steps or receipts, so the Hub lists and accepts them only at level
|
|
94
|
+
* 6 (`chainTxMessageTypesForLevel`); options answer as at version 3.
|
|
95
|
+
* Version 5 is the 0.7.0 contract: version 4 plus the application card (`ixo.bid.submit.v1`)
|
|
91
96
|
* and the claim card (`ixo.claim.submit.v1`) with their receipts
|
|
92
97
|
* (`ixo.bid.submission.v1`, `ixo.claim.submission.v1`,
|
|
93
98
|
* `ixo.submission.failed.v1`), which a 0.6.0 parser cannot read, so the Hub
|
|
@@ -101,22 +106,32 @@ export interface HubActionTransport {
|
|
|
101
106
|
* the 0.4.1 shapes. The envelopes stay tagged `ixo.auth-outcome/2` and
|
|
102
107
|
* `ixo.auth-capabilities/2`, which `@ixo/action-protocol` parses.
|
|
103
108
|
*
|
|
104
|
-
* An Auth Hub older than version
|
|
105
|
-
* list (1 to
|
|
106
|
-
* `invalid_request`, before anything else
|
|
109
|
+
* An Auth Hub older than version 6 checks `?version=` against its closed
|
|
110
|
+
* list (1 to 5) and answers every call that declares 6 with 400
|
|
111
|
+
* `invalid_request`, before anything else. This runtime then speaks
|
|
112
|
+
* version 5 to it (every Hub action but the level-6 chain types keeps
|
|
113
|
+
* working while Auth Hub is rolled out) and asks for 6 again after
|
|
114
|
+
* `HUB_PROTOCOL_RECHECK_MS`; a Hub that refuses 5 too is told apart with
|
|
115
|
+
* `HUB_UPDATE_REQUIRED`.
|
|
107
116
|
*/
|
|
108
|
-
const HUB_PROTOCOL_VERSION = '
|
|
109
|
-
|
|
117
|
+
const HUB_PROTOCOL_VERSION = '6';
|
|
118
|
+
/** The lowest version this runtime works at: 6 without the level-6 chain types. */
|
|
119
|
+
const HUB_PROTOCOL_FALLBACK_VERSION = '5';
|
|
120
|
+
type HubProtocolVersion =
|
|
121
|
+
| typeof HUB_PROTOCOL_VERSION
|
|
122
|
+
| typeof HUB_PROTOCOL_FALLBACK_VERSION;
|
|
123
|
+
/** How long a Hub that refused version 6 is spoken to at version 5 before 6 is tried again. */
|
|
124
|
+
export const HUB_PROTOCOL_RECHECK_MS = 10 * 60_000;
|
|
110
125
|
|
|
111
126
|
/**
|
|
112
127
|
* What the model is told when Auth Hub refuses a read (capabilities,
|
|
113
128
|
* options, status) with 400 `invalid_request`. A read carries nothing but
|
|
114
129
|
* the protocol version (and, for options, the binding the runtime itself
|
|
115
|
-
* checked), so that answer comes from a Hub that does not know version
|
|
130
|
+
* checked), so that answer comes from a Hub that does not know version 6.
|
|
116
131
|
* The owner object keeps every action it holds: polls and callbacks pick
|
|
117
132
|
* the outcomes up once the Hub is updated.
|
|
118
133
|
*/
|
|
119
|
-
export const HUB_UPDATE_REQUIRED = `Auth Hub must be updated before this oracle can use Hub actions: it does not accept protocol version ${
|
|
134
|
+
export const HUB_UPDATE_REQUIRED = `Auth Hub must be updated before this oracle can use Hub actions: it does not accept protocol version ${HUB_PROTOCOL_FALLBACK_VERSION}, the lowest this runtime works with (invalid_request). Tell the person this is not available right now; retrying or changing the fields will not help.`;
|
|
120
135
|
/** Every Hub call but a chain-transaction prepare. */
|
|
121
136
|
const HUB_TIMEOUT_MS = 10_000;
|
|
122
137
|
/**
|
|
@@ -131,8 +146,17 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
131
146
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
132
147
|
}
|
|
133
148
|
|
|
149
|
+
/** A read Auth Hub refused because it does not know the declared version. */
|
|
150
|
+
function isUpdateRequired(error: unknown): boolean {
|
|
151
|
+
return (
|
|
152
|
+
error instanceof HubActionRefused && error.message === HUB_UPDATE_REQUIRED
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
134
156
|
export class HubActionClient {
|
|
135
157
|
private readonly origin: string;
|
|
158
|
+
/** Until when this Hub is spoken to at version 5 (it refused 6). */
|
|
159
|
+
private fallbackUntil = 0;
|
|
136
160
|
|
|
137
161
|
constructor(private readonly transport: HubActionTransport) {
|
|
138
162
|
const url = new URL(transport.origin);
|
|
@@ -152,6 +176,34 @@ export class HubActionClient {
|
|
|
152
176
|
this.origin = url.origin;
|
|
153
177
|
}
|
|
154
178
|
|
|
179
|
+
/** The protocol version the next call declares. */
|
|
180
|
+
get protocolVersion(): HubProtocolVersion {
|
|
181
|
+
return Date.now() < this.fallbackUntil
|
|
182
|
+
? HUB_PROTOCOL_FALLBACK_VERSION
|
|
183
|
+
: HUB_PROTOCOL_VERSION;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* A read at the current version; when Auth Hub refuses version 6 as
|
|
188
|
+
* unknown, the same read at version 5, remembered for
|
|
189
|
+
* `HUB_PROTOCOL_RECHECK_MS`. Only reads do this: a refused read changed
|
|
190
|
+
* nothing, while a prepare's 400 can be a real refusal of its operation.
|
|
191
|
+
*/
|
|
192
|
+
private async negotiated<T>(
|
|
193
|
+
read: (version: HubProtocolVersion) => Promise<T>,
|
|
194
|
+
): Promise<T> {
|
|
195
|
+
const version = this.protocolVersion;
|
|
196
|
+
try {
|
|
197
|
+
return await read(version);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
if (version !== HUB_PROTOCOL_VERSION || !isUpdateRequired(error))
|
|
200
|
+
throw error;
|
|
201
|
+
const answer = await read(HUB_PROTOCOL_FALLBACK_VERSION);
|
|
202
|
+
this.fallbackUntil = Date.now() + HUB_PROTOCOL_RECHECK_MS;
|
|
203
|
+
return answer;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
155
207
|
/** `subject`: what a refused prepare is called in the model's words. */
|
|
156
208
|
private async send(
|
|
157
209
|
path: string,
|
|
@@ -221,8 +273,9 @@ export class HubActionClient {
|
|
|
221
273
|
// amounts) without it, and stores the version per action for the
|
|
222
274
|
// outcomes it pushes.
|
|
223
275
|
const operation = parseOperation(input.operation);
|
|
276
|
+
// The version the capabilities read before every prepare settled on.
|
|
224
277
|
const response = await this.send(
|
|
225
|
-
`/api/internal/actions/operations
|
|
278
|
+
`/api/internal/actions/operations?version=${this.protocolVersion}`,
|
|
226
279
|
{ ...input, operation },
|
|
227
280
|
operation.kind === 'ixo.chain.tx.v1'
|
|
228
281
|
? HUB_CHAIN_TX_PREPARE_TIMEOUT_MS
|
|
@@ -234,10 +287,17 @@ export class HubActionClient {
|
|
|
234
287
|
return assertHubReferenceUrl(response.reference, this.origin);
|
|
235
288
|
}
|
|
236
289
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
290
|
+
/** `timeoutMs`: a turn's tool list passes a short one (a slow Hub must not hold a turn). */
|
|
291
|
+
async capabilities(
|
|
292
|
+
timeoutMs = HUB_TIMEOUT_MS,
|
|
293
|
+
): Promise<ActionCapabilitiesV1> {
|
|
294
|
+
return this.negotiated(async (version) =>
|
|
295
|
+
parseCapabilities(
|
|
296
|
+
await this.send(
|
|
297
|
+
`/api/internal/actions/operations/capabilities?version=${version}`,
|
|
298
|
+
undefined,
|
|
299
|
+
timeoutMs,
|
|
300
|
+
),
|
|
241
301
|
),
|
|
242
302
|
);
|
|
243
303
|
}
|
|
@@ -249,16 +309,18 @@ export class HubActionClient {
|
|
|
249
309
|
// Options version 3 (asked at the protocol version; version 4 answers
|
|
250
310
|
// the same): version 2 plus the owner's bank tokens
|
|
251
311
|
// (`transfer.tokens`), which any-token transfers are chosen from.
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
312
|
+
const options = await this.negotiated(async (version) => {
|
|
313
|
+
const query = new URLSearchParams({
|
|
314
|
+
bindingId: input.bindingId,
|
|
315
|
+
bindingRevision: String(input.bindingRevision),
|
|
316
|
+
version,
|
|
317
|
+
});
|
|
318
|
+
return parseActionOptions(
|
|
319
|
+
await this.send(
|
|
320
|
+
`/api/internal/actions/operations/options?${query.toString()}`,
|
|
321
|
+
),
|
|
322
|
+
);
|
|
256
323
|
});
|
|
257
|
-
const options = parseActionOptions(
|
|
258
|
-
await this.send(
|
|
259
|
-
`/api/internal/actions/operations/options?${query.toString()}`,
|
|
260
|
-
),
|
|
261
|
-
);
|
|
262
324
|
if (options.version !== 'ixo.auth-options/3')
|
|
263
325
|
throw new Error('Hub action options require version 3');
|
|
264
326
|
return options;
|
|
@@ -295,10 +357,12 @@ export class HubActionClient {
|
|
|
295
357
|
): Promise<HubActionStatus> {
|
|
296
358
|
if (!/^[A-Za-z0-9_-]{32}$/.test(id))
|
|
297
359
|
throw new Error('Invalid Hub action id');
|
|
298
|
-
const raw = await this.
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
360
|
+
const raw = await this.negotiated((version) =>
|
|
361
|
+
this.send(
|
|
362
|
+
`/api/internal/actions/operations/${id}?version=${version}`,
|
|
363
|
+
undefined,
|
|
364
|
+
timeoutMs,
|
|
365
|
+
),
|
|
302
366
|
);
|
|
303
367
|
if (!isRecord(raw)) throw new Error('Invalid Hub action status');
|
|
304
368
|
const value = raw;
|
|
@@ -7,32 +7,28 @@ import {
|
|
|
7
7
|
type ChainTxMessage,
|
|
8
8
|
type ChainTxOperation,
|
|
9
9
|
} from '@ixo/action-protocol';
|
|
10
|
+
import type { ChainTxOwnerField, DistributiveOmit } from '../plugin-api/types';
|
|
10
11
|
import type { HubActionClient } from './client';
|
|
11
12
|
import { HubActionRefused, type HubActionOrigin } from './store';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
|
-
* A chain-transaction message as the owner object completes it: bank
|
|
15
|
-
* amounts already in base units (`
|
|
16
|
-
* identity
|
|
15
|
+
* A chain-transaction message as the owner object completes it: bank and
|
|
16
|
+
* stake amounts already in base units (`chainTxAmountsInBaseUnits`), the
|
|
17
|
+
* owner's identity (`ChainTxOwnerField`) still to add (`chainTxOperation`).
|
|
17
18
|
*/
|
|
18
|
-
export type ChainTxHostMessage =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
>
|
|
23
|
-
| Omit<Extract<ChainTxMessage, { type: 'ixo.claims.submit' }>, 'agentDid'>
|
|
24
|
-
| Omit<
|
|
25
|
-
Extract<ChainTxMessage, { type: 'ixo.claims.evaluate' }>,
|
|
26
|
-
'agentDid' | 'agentAddress'
|
|
27
|
-
>;
|
|
19
|
+
export type ChainTxHostMessage = DistributiveOmit<
|
|
20
|
+
ChainTxMessage,
|
|
21
|
+
ChainTxOwnerField
|
|
22
|
+
>;
|
|
28
23
|
|
|
29
24
|
/**
|
|
30
25
|
* The chain transaction (`ixo.chain.tx.v1`) of the model's messages: the
|
|
31
26
|
* signer is the owner's account (their DID is `did:ixo:<address>`), and
|
|
32
|
-
* the owner's identity in
|
|
33
|
-
* `did:ixo:<signer>` (the
|
|
34
|
-
*
|
|
35
|
-
*
|
|
27
|
+
* the owner's identity in the messages is the signer's: `agentDid`,
|
|
28
|
+
* `creatorDid`, `controllerDid` and `ownerDid` are `did:ixo:<signer>` (the
|
|
29
|
+
* DID the runtime authenticates to the Hub as) and an evaluation's
|
|
30
|
+
* `agentAddress` is the signer (the chain records the evaluator, and pays
|
|
31
|
+
* it, by that address; Auth Hub requires each to be the owner's). The
|
|
36
32
|
* protocol parses the result; a field the runtime sets (signer, network,
|
|
37
33
|
* chain id) is refused in fixed words, a field from the model's messages
|
|
38
34
|
* in the protocol's (`Invalid messages[0].tokens[0].id`).
|
|
@@ -49,15 +45,26 @@ export function chainTxOperation(input: {
|
|
|
49
45
|
const signer = input.ownerDid.startsWith('did:ixo:')
|
|
50
46
|
? input.ownerDid.slice('did:ixo:'.length)
|
|
51
47
|
: '';
|
|
52
|
-
const
|
|
48
|
+
const ownDid = `did:ixo:${signer}`;
|
|
53
49
|
// Identity last: a stray identity field cannot survive.
|
|
54
|
-
const messages
|
|
55
|
-
message.type
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
const messages = input.messages.map((message): ChainTxMessage => {
|
|
51
|
+
switch (message.type) {
|
|
52
|
+
case 'ixo.claims.submit':
|
|
53
|
+
case 'ixo.claims.claim-intent':
|
|
54
|
+
case 'ixo.claims.dispute':
|
|
55
|
+
return { ...message, agentDid: ownDid };
|
|
56
|
+
case 'ixo.claims.evaluate':
|
|
57
|
+
return { ...message, agentDid: ownDid, agentAddress: signer };
|
|
58
|
+
case 'ixo.claims.create-claim-authorization':
|
|
59
|
+
return { ...message, creatorDid: ownDid };
|
|
60
|
+
case 'ixo.entity.update':
|
|
61
|
+
return { ...message, controllerDid: ownDid };
|
|
62
|
+
case 'ixo.entity.create':
|
|
63
|
+
return { ...message, ownerDid: ownDid };
|
|
64
|
+
default:
|
|
65
|
+
return message;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
61
68
|
try {
|
|
62
69
|
return parseOperation({
|
|
63
70
|
kind: 'ixo.chain.tx.v1',
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
parseChainTxRefusal,
|
|
3
|
+
type ActionOperationV1,
|
|
4
|
+
type ActionReceiptV1,
|
|
5
|
+
type OptionsToken,
|
|
5
6
|
} from '@ixo/action-protocol';
|
|
7
|
+
import { chainTxRefusalWords, wordableRefusal } from './chain-tx-refusals';
|
|
6
8
|
import type { ChainTxMessageInput } from '../plugin-api/types';
|
|
7
9
|
import { quoteHubText } from './hub-text';
|
|
8
10
|
import type { ChainTxHostMessage } from './request';
|
|
@@ -91,24 +93,67 @@ const IXO_TOKEN: TransferToken = {
|
|
|
91
93
|
};
|
|
92
94
|
|
|
93
95
|
/**
|
|
94
|
-
* A chain transaction's
|
|
96
|
+
* A chain transaction's amounts in base units; messages without one pass
|
|
95
97
|
* unchanged. Each bank step names a display `amount` of a token from the
|
|
96
98
|
* owner's list (`denom`, absent = uixo): the token must be listed, the
|
|
97
99
|
* amount converts with its listed decimals (`toBaseUnits`: a plain decimal,
|
|
98
100
|
* no more digits after the point than the decimals, above zero; an
|
|
99
101
|
* unverified token lists 0: base units), and the bank steps of one token
|
|
100
|
-
* together stay within its listed balance.
|
|
101
|
-
*
|
|
102
|
-
*
|
|
102
|
+
* together stay within its listed balance. Each stake step (stake, unstake,
|
|
103
|
+
* move stake) names a display `amount` of IXO, converted with IXO's 6
|
|
104
|
+
* decimals; what a stake takes from the wallet counts against the listed
|
|
105
|
+
* IXO balance together with the IXO bank steps, when the list has IXO
|
|
106
|
+
* (Auth Hub checks every balance again before the card is made). `tokens`
|
|
107
|
+
* is the owner's list (hub_action_options `transfer.tokens`), null when the
|
|
108
|
+
* Hub listed none; the caller reads it only when there is a bank or stake
|
|
109
|
+
* step.
|
|
103
110
|
*/
|
|
104
|
-
export function
|
|
111
|
+
export function chainTxAmountsInBaseUnits(
|
|
105
112
|
messages: readonly ChainTxMessageInput[],
|
|
106
113
|
tokens: readonly OptionsToken[] | null,
|
|
107
114
|
): ChainTxHostMessage[] {
|
|
108
115
|
const spent = new Map<string, bigint>();
|
|
109
|
-
|
|
110
|
-
|
|
116
|
+
/** The bank and stake steps of one listed token together, within its listed balance. */
|
|
117
|
+
const spend = (
|
|
118
|
+
at: string,
|
|
119
|
+
token: OptionsToken,
|
|
120
|
+
amount: bigint,
|
|
121
|
+
what: string,
|
|
122
|
+
) => {
|
|
123
|
+
const total = (spent.get(token.denom) ?? 0n) + amount;
|
|
124
|
+
spent.set(token.denom, total);
|
|
125
|
+
if (total > BigInt(token.balanceBaseUnits))
|
|
126
|
+
throw new HubActionRefused(
|
|
127
|
+
'refused',
|
|
128
|
+
`${at}.amount: the owner holds ${describeAmount(token.balanceBaseUnits, token)}, less than ${total === amount ? what : 'what the steps of this transaction take of it together'}.`,
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
const converted = (at: string, amount: string, decimals: number) => {
|
|
132
|
+
try {
|
|
133
|
+
return toBaseUnits(amount, decimals);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (error instanceof HubActionRefused)
|
|
136
|
+
throw new HubActionRefused('refused', `${at}.amount: ${error.message}`);
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
return messages.map((message, index): ChainTxHostMessage => {
|
|
111
141
|
const at = `messages[${index}]`;
|
|
142
|
+
if (
|
|
143
|
+
message.type === 'cosmos.staking.delegate' ||
|
|
144
|
+
message.type === 'cosmos.staking.undelegate' ||
|
|
145
|
+
message.type === 'cosmos.staking.redelegate'
|
|
146
|
+
) {
|
|
147
|
+
const { amount: display, ...rest } = message;
|
|
148
|
+
const amount = converted(at, display, IXO_TOKEN.decimals);
|
|
149
|
+
// Only a stake takes IXO from the wallet: unstaking and moving stake
|
|
150
|
+
// work on what is already staked.
|
|
151
|
+
const ixo = tokens?.find((item) => item.denom === IXO_TOKEN.denom);
|
|
152
|
+
if (message.type === 'cosmos.staking.delegate' && ixo)
|
|
153
|
+
spend(at, ixo, amount, 'this stake');
|
|
154
|
+
return { ...rest, amountBaseUnits: amount.toString() };
|
|
155
|
+
}
|
|
156
|
+
if (message.type !== 'cosmos.bank.send') return message;
|
|
112
157
|
if (!tokens?.length)
|
|
113
158
|
throw new HubActionRefused(
|
|
114
159
|
'refused',
|
|
@@ -124,21 +169,8 @@ export function bankStepsInBaseUnits(
|
|
|
124
169
|
.slice(0, 10)
|
|
125
170
|
.join(', ')}).`,
|
|
126
171
|
);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
amount = toBaseUnits(message.amount, token.decimals);
|
|
130
|
-
} catch (error) {
|
|
131
|
-
if (error instanceof HubActionRefused)
|
|
132
|
-
throw new HubActionRefused('refused', `${at}.amount: ${error.message}`);
|
|
133
|
-
throw error;
|
|
134
|
-
}
|
|
135
|
-
const total = (spent.get(token.denom) ?? 0n) + amount;
|
|
136
|
-
spent.set(token.denom, total);
|
|
137
|
-
if (total > BigInt(token.balanceBaseUnits))
|
|
138
|
-
throw new HubActionRefused(
|
|
139
|
-
'refused',
|
|
140
|
-
`${at}.amount: the owner holds ${describeAmount(token.balanceBaseUnits, token)}, less than ${total === amount ? 'this send' : 'the bank steps of this token in this transaction'}.`,
|
|
141
|
-
);
|
|
172
|
+
const amount = converted(at, message.amount, token.decimals);
|
|
173
|
+
spend(at, token, amount, 'this send');
|
|
142
174
|
return {
|
|
143
175
|
type: 'cosmos.bank.send',
|
|
144
176
|
toAddress: message.toAddress,
|
|
@@ -316,6 +348,24 @@ export function hubPrepareRefusal(
|
|
|
316
348
|
if (unfixable)
|
|
317
349
|
return new HubActionRefused('refused', unfixable, undefined, status);
|
|
318
350
|
if (!parsed) return null;
|
|
351
|
+
// A chain transaction Auth Hub would not make from what it read on chain,
|
|
352
|
+
// or whose simulation named one message: a fixed code, in the runtime's
|
|
353
|
+
// own words (the reason text is not passed on). Without a code, the
|
|
354
|
+
// reason is passed on quoted, as below.
|
|
355
|
+
const coded =
|
|
356
|
+
status === 400 &&
|
|
357
|
+
subject === 'transaction' &&
|
|
358
|
+
(parsed.error === 'operation_not_registered' ||
|
|
359
|
+
parsed.error === 'simulation_failed')
|
|
360
|
+
? wordableRefusal(parseChainTxRefusal(parsed.reason))
|
|
361
|
+
: null;
|
|
362
|
+
if (coded)
|
|
363
|
+
return new HubActionRefused(
|
|
364
|
+
'refused',
|
|
365
|
+
chainTxRefusalWords(coded),
|
|
366
|
+
undefined,
|
|
367
|
+
status,
|
|
368
|
+
);
|
|
319
369
|
const reason = parsed.reason
|
|
320
370
|
? quoteHubText('reason', parsed.reason, HUB_REASON_MAX)
|
|
321
371
|
: '';
|
package/src/plugin-api/types.ts
CHANGED
|
@@ -32,13 +32,40 @@ import type {
|
|
|
32
32
|
ChainTxMessage,
|
|
33
33
|
} from '@ixo/action-protocol';
|
|
34
34
|
|
|
35
|
+
/** `Omit` applied to each member of a union (plain `Omit` merges them). */
|
|
36
|
+
export type DistributiveOmit<T, K extends PropertyKey> = T extends unknown
|
|
37
|
+
? Omit<T, K>
|
|
38
|
+
: never;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The owner's own identity in chain-transaction messages, which the host
|
|
42
|
+
* fills and the model never names: the filer's or disputer's `agentDid`
|
|
43
|
+
* (claims, reservations, disputes), an evaluation's `agentDid` and
|
|
44
|
+
* `agentAddress`, a claim permission's `creatorDid`, an entity update's
|
|
45
|
+
* `controllerDid` and a new entity's `ownerDid`: always `did:ixo:<signer>`,
|
|
46
|
+
* or the signer for an address.
|
|
47
|
+
*/
|
|
48
|
+
export type ChainTxOwnerField =
|
|
49
|
+
| 'agentDid'
|
|
50
|
+
| 'agentAddress'
|
|
51
|
+
| 'creatorDid'
|
|
52
|
+
| 'controllerDid'
|
|
53
|
+
| 'ownerDid';
|
|
54
|
+
|
|
55
|
+
/** The stake steps: their amount is IXO, written in display units. */
|
|
56
|
+
export type ChainTxStakeType =
|
|
57
|
+
| 'cosmos.staking.delegate'
|
|
58
|
+
| 'cosmos.staking.undelegate'
|
|
59
|
+
| 'cosmos.staking.redelegate';
|
|
60
|
+
|
|
35
61
|
/**
|
|
36
62
|
* A chain-transaction message as the model writes it: the protocol's
|
|
37
|
-
* message without the owner's identity, which the
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
63
|
+
* message without the owner's identity (`ChainTxOwnerField`), which the
|
|
64
|
+
* host fills. A bank step names a display `amount` of a token from the
|
|
65
|
+
* owner's list (`denom`, absent = uixo); a stake step a display `amount` of
|
|
66
|
+
* IXO. The host converts both to the protocol's `amountBaseUnits` (the
|
|
67
|
+
* listed decimals; 6 for IXO). Every other amount (impact tokens, a
|
|
68
|
+
* collection's payments) is already in the protocol's units.
|
|
42
69
|
*/
|
|
43
70
|
export type ChainTxMessageInput =
|
|
44
71
|
| {
|
|
@@ -48,11 +75,16 @@ export type ChainTxMessageInput =
|
|
|
48
75
|
amount: string;
|
|
49
76
|
denom?: string;
|
|
50
77
|
}
|
|
51
|
-
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
78
|
+
| (DistributiveOmit<
|
|
79
|
+
Extract<ChainTxMessage, { type: ChainTxStakeType }>,
|
|
80
|
+
'amountBaseUnits'
|
|
81
|
+
> & {
|
|
82
|
+
/** A plain decimal in IXO ("100" or "0.5"). */
|
|
83
|
+
amount: string;
|
|
84
|
+
})
|
|
85
|
+
| DistributiveOmit<
|
|
86
|
+
Exclude<ChainTxMessage, { type: 'cosmos.bank.send' | ChainTxStakeType }>,
|
|
87
|
+
ChainTxOwnerField
|
|
56
88
|
>;
|
|
57
89
|
|
|
58
90
|
/**
|
|
@@ -812,7 +844,13 @@ export interface RuntimeContext<TConfig = MergedConfig> {
|
|
|
812
844
|
|
|
813
845
|
/** Host-owned action coordinator. It accepts only runtime-derived channel context. */
|
|
814
846
|
hubActions?: {
|
|
815
|
-
|
|
847
|
+
/**
|
|
848
|
+
* The Hub's capabilities for this oracle account (cached 60 s).
|
|
849
|
+
* `turn`: a read on a turn's critical path (a tool list): a short
|
|
850
|
+
* timeout, no new read within 30 s of a failed one, and the last
|
|
851
|
+
* capabilities read while reads fail.
|
|
852
|
+
*/
|
|
853
|
+
capabilities(opts?: { turn?: boolean }): Promise<ActionCapabilitiesV1>;
|
|
816
854
|
options(input: {
|
|
817
855
|
ownerDid: string;
|
|
818
856
|
provider: string;
|
|
@@ -822,13 +860,16 @@ export interface RuntimeContext<TConfig = MergedConfig> {
|
|
|
822
860
|
/**
|
|
823
861
|
* One chain transaction (`ixo.chain.tx.v1`) of 1–8 messages: the host
|
|
824
862
|
* adds the chain id and network (the account's), the signer (the
|
|
825
|
-
* owner's address) and the owner's identity
|
|
826
|
-
*
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
831
|
-
* digits than its decimals, zero or more than the listed balance.
|
|
863
|
+
* owner's address) and the owner's identity (`ChainTxOwnerField`:
|
|
864
|
+
* `did:ixo:<signer>`, an evaluation's `agentAddress` = the signer) and
|
|
865
|
+
* prepares it like any operation (no fee: the Hub simulates gas and
|
|
866
|
+
* sets it). A bank step's display `amount` is converted to base units
|
|
867
|
+
* from the owner's token list (one options read per call, only when
|
|
868
|
+
* there is a bank or stake step), refused for a token not listed, more
|
|
869
|
+
* digits than its decimals, zero or more than the listed balance. A
|
|
870
|
+
* stake step's IXO `amount` is converted with 6 decimals; what a stake
|
|
871
|
+
* takes from the wallet counts against the listed IXO balance with the
|
|
872
|
+
* IXO bank steps, when the list has IXO.
|
|
832
873
|
*/
|
|
833
874
|
chainTransaction(input: {
|
|
834
875
|
ownerDid: string;
|