@ixo/oracle-runtime-workers 0.28.2 → 0.29.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/package.json +4 -4
- package/src/delivery/plan.ts +1 -1
- package/src/do/user-oracle-do.ts +42 -71
- package/src/hub-actions/client.ts +52 -21
- package/src/hub-actions/request.ts +102 -0
- package/src/hub-actions/transfer.ts +167 -18
- package/src/plugin-api/types.ts +35 -7
- package/src/plugins/hub-actions/hub-actions.plugin.ts +518 -50
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ixo/oracle-runtime-workers",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.1",
|
|
4
4
|
"description": "QiForge oracle runtime for Cloudflare Workers — Durable Object per user (SQLite in WASM over DO storage), Matrix E2EE gateway Durable Object, Hono HTTP/SSE shell.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"!src/plugins/pod-creator/test-fixtures.ts"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@ixo/action-protocol": "0.
|
|
36
|
+
"@ixo/action-protocol": "0.6.0",
|
|
37
37
|
"@blocknote/server-util": "0.29.1",
|
|
38
38
|
"@composio/core": "0.10.0",
|
|
39
39
|
"@composio/langchain": "0.9.2",
|
|
@@ -66,8 +66,8 @@
|
|
|
66
66
|
"protobufjs": "^7.6.5",
|
|
67
67
|
"@ixo/topic-protocol": "1.0.0-rc.8",
|
|
68
68
|
"@ixo/common": "1.7.1",
|
|
69
|
-
"@ixo/
|
|
70
|
-
"@ixo/
|
|
69
|
+
"@ixo/ucan": "2.3.0",
|
|
70
|
+
"@ixo/ixo-transaction": "0.2.0"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@cloudflare/vitest-pool-workers": "^0.22.0",
|
package/src/delivery/plan.ts
CHANGED
|
@@ -165,7 +165,7 @@ export function draftReplyPlan(input: {
|
|
|
165
165
|
for (const call of step.toolCalls) {
|
|
166
166
|
if (
|
|
167
167
|
call.name === 'request_hub_action' ||
|
|
168
|
-
call.name === '
|
|
168
|
+
call.name === 'request_chain_transaction'
|
|
169
169
|
) {
|
|
170
170
|
try {
|
|
171
171
|
const result = JSON.parse(input.toolResults.get(call.id) ?? 'null');
|
package/src/do/user-oracle-do.ts
CHANGED
|
@@ -47,11 +47,10 @@ import type {
|
|
|
47
47
|
} from '@ixo/ixo-transaction/action-proposals';
|
|
48
48
|
import { ChannelTurns } from '../channels/turns';
|
|
49
49
|
import { HubActionClient } from '../hub-actions/client';
|
|
50
|
-
import { prepareHubAction } from '../hub-actions/request';
|
|
50
|
+
import { chainTxOperation, prepareHubAction } from '../hub-actions/request';
|
|
51
51
|
import {
|
|
52
52
|
bankReceiptSummary,
|
|
53
|
-
|
|
54
|
-
toBaseUnits,
|
|
53
|
+
bankStepsInBaseUnits,
|
|
55
54
|
} from '../hub-actions/transfer';
|
|
56
55
|
import {
|
|
57
56
|
HUB_CHECK_AFTER_EXPIRY_MS,
|
|
@@ -64,7 +63,6 @@ import {
|
|
|
64
63
|
type ActionCapabilitiesV1,
|
|
65
64
|
type HubActionOptionsV2,
|
|
66
65
|
type HubActionOptionsV3,
|
|
67
|
-
parseOperation,
|
|
68
66
|
v1Receipt,
|
|
69
67
|
type ActionOperationV1,
|
|
70
68
|
type ActionOutcomeV1,
|
|
@@ -430,7 +428,11 @@ import {
|
|
|
430
428
|
resolveTurnDelegation,
|
|
431
429
|
WorkersUcanService,
|
|
432
430
|
} from './ucan-service';
|
|
433
|
-
import type {
|
|
431
|
+
import type {
|
|
432
|
+
ChainTxMessageInput,
|
|
433
|
+
UcanDelegation,
|
|
434
|
+
UserContextData,
|
|
435
|
+
} from '../plugin-api/types';
|
|
434
436
|
|
|
435
437
|
/**
|
|
436
438
|
* The gateway's topic read errors that describe what the homeserver
|
|
@@ -2269,7 +2271,8 @@ export function createUserOracleDO(opts: UserOracleDOOptions) {
|
|
|
2269
2271
|
requestKey: string;
|
|
2270
2272
|
operation: ActionOperationV1;
|
|
2271
2273
|
}) => this.requestHubAction(input),
|
|
2272
|
-
|
|
2274
|
+
chainTransaction: (input) =>
|
|
2275
|
+
this.chainTransactionHubAction(input),
|
|
2273
2276
|
status: (ownerDid: string, actionId: string) =>
|
|
2274
2277
|
this.hubActionStatusForTool(ownerDid, actionId),
|
|
2275
2278
|
},
|
|
@@ -3337,7 +3340,7 @@ export function createUserOracleDO(opts: UserOracleDOOptions) {
|
|
|
3337
3340
|
};
|
|
3338
3341
|
}
|
|
3339
3342
|
|
|
3340
|
-
/** `hub_action_options` and
|
|
3343
|
+
/** `hub_action_options` and a chain transaction's bank steps: version 3, with the owner's tokens. */
|
|
3341
3344
|
private async hubActionOptions(
|
|
3342
3345
|
input: {
|
|
3343
3346
|
ownerDid: string;
|
|
@@ -3508,7 +3511,7 @@ export function createUserOracleDO(opts: UserOracleDOOptions) {
|
|
|
3508
3511
|
}
|
|
3509
3512
|
|
|
3510
3513
|
/**
|
|
3511
|
-
* `checked`: the
|
|
3514
|
+
* `checked`: the chain-transaction path already ran the turn-origin refusal and
|
|
3512
3515
|
* validated the binding in this same call.
|
|
3513
3516
|
*/
|
|
3514
3517
|
private async requestHubAction(
|
|
@@ -3555,15 +3558,17 @@ export function createUserOracleDO(opts: UserOracleDOOptions) {
|
|
|
3555
3558
|
}
|
|
3556
3559
|
|
|
3557
3560
|
/**
|
|
3558
|
-
*
|
|
3559
|
-
*
|
|
3560
|
-
*
|
|
3561
|
-
*
|
|
3562
|
-
*
|
|
3563
|
-
*
|
|
3564
|
-
*
|
|
3561
|
+
* One chain transaction of the model's messages. The network is the
|
|
3562
|
+
* account's capability, the chain id this Worker's own for that
|
|
3563
|
+
* network; the signer and the owner's identity in claims messages come
|
|
3564
|
+
* from the owner's DID (`chainTxOperation`). The model names none of
|
|
3565
|
+
* them. Bank steps name a listed token and a display amount: with at
|
|
3566
|
+
* least one, the owner's options are read once, fresh (the balances
|
|
3567
|
+
* must be current), and each step is converted to base units and
|
|
3568
|
+
* checked against the listed balance (`bankStepsInBaseUnits`). No fee
|
|
3569
|
+
* is sent: the Hub simulates gas and sets it.
|
|
3565
3570
|
*/
|
|
3566
|
-
private async
|
|
3571
|
+
private async chainTransactionHubAction(input: {
|
|
3567
3572
|
ownerDid: string;
|
|
3568
3573
|
sessionId: string;
|
|
3569
3574
|
runId: string;
|
|
@@ -3572,73 +3577,39 @@ export function createUserOracleDO(opts: UserOracleDOOptions) {
|
|
|
3572
3577
|
bindingId: string;
|
|
3573
3578
|
bindingRevision: number;
|
|
3574
3579
|
requestKey: string;
|
|
3575
|
-
|
|
3576
|
-
denom: string;
|
|
3577
|
-
amount: string;
|
|
3580
|
+
messages: ChainTxMessageInput[];
|
|
3578
3581
|
memo: string;
|
|
3579
3582
|
}) {
|
|
3580
3583
|
if (!this.hubActionStore || input.ownerDid !== this.userDid)
|
|
3581
3584
|
throw new Error('Hub actions are unavailable for this channel');
|
|
3582
3585
|
await this.assertHubTurnOrigin(input);
|
|
3583
3586
|
await this.assertHubBinding(input);
|
|
3584
|
-
const options = await this.hubActionOptions(input, true);
|
|
3585
|
-
const tokens = options.transfer?.tokens;
|
|
3586
|
-
if (!tokens?.length)
|
|
3587
|
-
throw new HubActionRefused(
|
|
3588
|
-
'refused',
|
|
3589
|
-
'Bank sends are not available for this owner right now (no token list from Auth Hub).',
|
|
3590
|
-
);
|
|
3591
|
-
const token = tokens.find((item) => item.denom === input.denom);
|
|
3592
|
-
if (!token)
|
|
3593
|
-
throw new HubActionRefused(
|
|
3594
|
-
'refused',
|
|
3595
|
-
`That token is not in the owner's list; use a denom exactly as hub_action_options lists it (${tokens
|
|
3596
|
-
.map((item) => item.denom)
|
|
3597
|
-
.slice(0, 10)
|
|
3598
|
-
.join(', ')}).`,
|
|
3599
|
-
);
|
|
3600
|
-
const amountBaseUnits = toBaseUnits(input.amount, token.decimals);
|
|
3601
|
-
if (amountBaseUnits > BigInt(token.balanceBaseUnits))
|
|
3602
|
-
throw new HubActionRefused(
|
|
3603
|
-
'refused',
|
|
3604
|
-
`The owner holds ${describeAmount(token.balanceBaseUnits, token)}, less than this transfer.`,
|
|
3605
|
-
);
|
|
3606
3587
|
const capabilities = await this.cachedHubCapabilities();
|
|
3607
|
-
if (
|
|
3588
|
+
if (
|
|
3589
|
+
!capabilities.network ||
|
|
3590
|
+
!capabilities.chainTx ||
|
|
3591
|
+
!capabilities.operations.includes('ixo.chain.tx.v1')
|
|
3592
|
+
)
|
|
3608
3593
|
throw new HubActionRefused(
|
|
3609
3594
|
'refused',
|
|
3610
|
-
'
|
|
3595
|
+
'Chain transactions are not enabled for this oracle account; retrying or changing the fields will not help.',
|
|
3611
3596
|
);
|
|
3612
|
-
const network = HUB_NETWORK_CHAIN[capabilities.network];
|
|
3613
3597
|
const chainId = chainIdsFromConfig(chainIdConfigSchema.parse(this.env))[
|
|
3614
|
-
network
|
|
3598
|
+
HUB_NETWORK_CHAIN[capabilities.network]
|
|
3615
3599
|
];
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
}
|
|
3629
|
-
// Only the protocol refusing the operation built from the model's
|
|
3630
|
-
// fields (an invalid recipient or memo) is the model's to fix.
|
|
3631
|
-
throw new HubActionRefused(
|
|
3632
|
-
'refused',
|
|
3633
|
-
`The transfer is incomplete (${error instanceof Error ? error.message : String(error)}).`,
|
|
3634
|
-
);
|
|
3635
|
-
}
|
|
3636
|
-
const reference = await this.requestHubAction(
|
|
3637
|
-
{ ...input, operation },
|
|
3638
|
-
true,
|
|
3639
|
-
);
|
|
3640
|
-
await this.hubActionStore.setToken(reference.id, token);
|
|
3641
|
-
return reference;
|
|
3600
|
+
const tokens = input.messages.some(
|
|
3601
|
+
(message) => message.type === 'cosmos.bank.send',
|
|
3602
|
+
)
|
|
3603
|
+
? ((await this.hubActionOptions(input, true)).transfer?.tokens ?? null)
|
|
3604
|
+
: null;
|
|
3605
|
+
const operation = chainTxOperation({
|
|
3606
|
+
ownerDid: input.ownerDid,
|
|
3607
|
+
chainId,
|
|
3608
|
+
network: capabilities.network,
|
|
3609
|
+
memo: input.memo,
|
|
3610
|
+
messages: bankStepsInBaseUnits(input.messages, tokens),
|
|
3611
|
+
});
|
|
3612
|
+
return this.requestHubAction({ ...input, operation }, true);
|
|
3642
3613
|
}
|
|
3643
3614
|
|
|
3644
3615
|
/**
|
|
@@ -11,7 +11,11 @@ import {
|
|
|
11
11
|
type HubActionOptionsV2,
|
|
12
12
|
type HubActionOptionsV3,
|
|
13
13
|
} from '@ixo/action-protocol';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
hubPrepareRefusal,
|
|
16
|
+
prepareSubjectOf,
|
|
17
|
+
type HubPrepareSubject,
|
|
18
|
+
} from './transfer';
|
|
15
19
|
|
|
16
20
|
export interface HubActionStatus {
|
|
17
21
|
reference: ActionReferenceV1;
|
|
@@ -31,15 +35,27 @@ export interface HubActionTransport {
|
|
|
31
35
|
|
|
32
36
|
/**
|
|
33
37
|
* The protocol version this runtime declares on every operation call
|
|
34
|
-
* (capabilities, POST operations, GET status, options). Version
|
|
35
|
-
* 0.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
38
|
+
* (capabilities, POST operations, GET status, options). Version 4 is the
|
|
39
|
+
* 0.6.0 contract: version 3 plus chain transactions (`ixo.chain.tx.v1` in
|
|
40
|
+
* capabilities with its `chainTx` entry, prepared through POST, receipts
|
|
41
|
+
* with `steps`); options answer as at version 3 (`ixo.auth-options/3`).
|
|
42
|
+
* Version 3 is the 0.5.0 contract: capabilities `transfer.limits`,
|
|
43
|
+
* any-denom bank sends, v2 receipts with `denom` and `amountBaseUnits`,
|
|
44
|
+
* options with the owner's tokens. Version 2 callers (runtime 0.23.0) keep
|
|
45
|
+
* the 0.4.1 shapes. The envelopes stay tagged `ixo.auth-outcome/2` and
|
|
39
46
|
* `ixo.auth-capabilities/2`, which `@ixo/action-protocol` parses.
|
|
40
47
|
*/
|
|
41
|
-
const HUB_PROTOCOL_VERSION = '
|
|
48
|
+
const HUB_PROTOCOL_VERSION = '4';
|
|
42
49
|
const HUB_PROTOCOL_QUERY = `version=${HUB_PROTOCOL_VERSION}`;
|
|
50
|
+
/** Every Hub call but a chain-transaction prepare. */
|
|
51
|
+
const HUB_TIMEOUT_MS = 10_000;
|
|
52
|
+
/**
|
|
53
|
+
* A chain-transaction prepare: Auth Hub bounds its own chain reads and
|
|
54
|
+
* simulation at about 8 s and then answers 503 `transfer_unavailable`
|
|
55
|
+
* (retryable); this leaves room for that answer to arrive instead of an
|
|
56
|
+
* abort here.
|
|
57
|
+
*/
|
|
58
|
+
export const HUB_CHAIN_TX_PREPARE_TIMEOUT_MS = 15_000;
|
|
43
59
|
|
|
44
60
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
45
61
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
@@ -66,10 +82,12 @@ export class HubActionClient {
|
|
|
66
82
|
this.origin = url.origin;
|
|
67
83
|
}
|
|
68
84
|
|
|
85
|
+
/** `subject`: what a refused prepare is called in the model's words. */
|
|
69
86
|
private async send(
|
|
70
87
|
path: string,
|
|
71
88
|
body?: unknown,
|
|
72
|
-
timeoutMs =
|
|
89
|
+
timeoutMs = HUB_TIMEOUT_MS,
|
|
90
|
+
subject?: HubPrepareSubject,
|
|
73
91
|
): Promise<unknown> {
|
|
74
92
|
const request = new Request(new URL(path, this.origin), {
|
|
75
93
|
method: body === undefined ? 'GET' : 'POST',
|
|
@@ -87,16 +105,22 @@ export class HubActionClient {
|
|
|
87
105
|
if (
|
|
88
106
|
body !== undefined &&
|
|
89
107
|
(response.status === 400 ||
|
|
108
|
+
response.status === 404 ||
|
|
90
109
|
response.status === 409 ||
|
|
110
|
+
response.status === 429 ||
|
|
91
111
|
response.status === 503)
|
|
92
112
|
) {
|
|
93
|
-
// A prepare the Hub refused with a typed code (a transfer
|
|
94
|
-
// fail, a fee above the ceiling, not enough
|
|
95
|
-
//
|
|
96
|
-
//
|
|
113
|
+
// A prepare the Hub refused with a typed code (a transfer or
|
|
114
|
+
// transaction that would fail, a fee above the ceiling, not enough
|
|
115
|
+
// funds, a message the Hub did not accept, a missing chain
|
|
116
|
+
// authorization, a busy or unavailable transfer path, an operation
|
|
117
|
+
// the account may not use, a kind turned off, a protocol mismatch,
|
|
118
|
+
// no account for the owner, a rate limit): the model relays it in
|
|
119
|
+
// words.
|
|
97
120
|
const refusal = hubPrepareRefusal(
|
|
98
121
|
response.status,
|
|
99
122
|
await response.json().catch(() => null),
|
|
123
|
+
subject,
|
|
100
124
|
);
|
|
101
125
|
if (refusal) throw refusal;
|
|
102
126
|
}
|
|
@@ -112,15 +136,18 @@ export class HubActionClient {
|
|
|
112
136
|
expiresAt: number;
|
|
113
137
|
operation: ActionOperationV1;
|
|
114
138
|
}): Promise<ActionReferenceV1> {
|
|
115
|
-
//
|
|
116
|
-
// (no any-denom sends, no receipt
|
|
117
|
-
// version per action for the
|
|
139
|
+
// The protocol version on every operation call: the Hub answers older
|
|
140
|
+
// shapes (no chain transactions, no any-denom sends, no receipt
|
|
141
|
+
// amounts) without it, and stores the version per action for the
|
|
142
|
+
// outcomes it pushes.
|
|
143
|
+
const operation = parseOperation(input.operation);
|
|
118
144
|
const response = await this.send(
|
|
119
145
|
`/api/internal/actions/operations?${HUB_PROTOCOL_QUERY}`,
|
|
120
|
-
{
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
146
|
+
{ ...input, operation },
|
|
147
|
+
operation.kind === 'ixo.chain.tx.v1'
|
|
148
|
+
? HUB_CHAIN_TX_PREPARE_TIMEOUT_MS
|
|
149
|
+
: HUB_TIMEOUT_MS,
|
|
150
|
+
prepareSubjectOf(operation.kind),
|
|
124
151
|
);
|
|
125
152
|
if (!response || typeof response !== 'object' || !('reference' in response))
|
|
126
153
|
throw new Error('Hub action response has no reference');
|
|
@@ -139,7 +166,8 @@ export class HubActionClient {
|
|
|
139
166
|
bindingId: string;
|
|
140
167
|
bindingRevision: number;
|
|
141
168
|
}): Promise<HubActionOptionsV3> {
|
|
142
|
-
// Options version 3
|
|
169
|
+
// Options version 3 (asked at the protocol version; version 4 answers
|
|
170
|
+
// the same): version 2 plus the owner's bank tokens
|
|
143
171
|
// (`transfer.tokens`), which any-token transfers are chosen from.
|
|
144
172
|
const query = new URLSearchParams({
|
|
145
173
|
bindingId: input.bindingId,
|
|
@@ -181,7 +209,10 @@ export class HubActionClient {
|
|
|
181
209
|
}
|
|
182
210
|
|
|
183
211
|
/** `timeoutMs`: the poll path passes a short one (a slow Hub must not hold a poll). */
|
|
184
|
-
async status(
|
|
212
|
+
async status(
|
|
213
|
+
id: string,
|
|
214
|
+
timeoutMs = HUB_TIMEOUT_MS,
|
|
215
|
+
): Promise<HubActionStatus> {
|
|
185
216
|
if (!/^[A-Za-z0-9_-]{32}$/.test(id))
|
|
186
217
|
throw new Error('Invalid Hub action id');
|
|
187
218
|
const raw = await this.send(
|
|
@@ -1,12 +1,91 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canonicalJson,
|
|
3
|
+
parseOperation,
|
|
3
4
|
type ActionCapabilitiesV1,
|
|
4
5
|
type ActionOperationV1,
|
|
5
6
|
type ActionReferenceV1,
|
|
7
|
+
type ChainTxMessage,
|
|
8
|
+
type ChainTxOperation,
|
|
6
9
|
} from '@ixo/action-protocol';
|
|
7
10
|
import type { HubActionClient } from './client';
|
|
8
11
|
import { HubActionRefused, type HubActionOrigin } from './store';
|
|
9
12
|
|
|
13
|
+
/**
|
|
14
|
+
* A chain-transaction message as the owner object completes it: bank
|
|
15
|
+
* amounts already in base units (`bankStepsInBaseUnits`), the owner's
|
|
16
|
+
* identity in claims messages still to add (`chainTxOperation`).
|
|
17
|
+
*/
|
|
18
|
+
export type ChainTxHostMessage =
|
|
19
|
+
| Extract<
|
|
20
|
+
ChainTxMessage,
|
|
21
|
+
{ type: 'cosmos.bank.send' } | { type: 'ixo.token.transfer' }
|
|
22
|
+
>
|
|
23
|
+
| Omit<Extract<ChainTxMessage, { type: 'ixo.claims.submit' }>, 'agentDid'>
|
|
24
|
+
| Omit<
|
|
25
|
+
Extract<ChainTxMessage, { type: 'ixo.claims.evaluate' }>,
|
|
26
|
+
'agentDid' | 'agentAddress'
|
|
27
|
+
>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The chain transaction (`ixo.chain.tx.v1`) of the model's messages: the
|
|
31
|
+
* signer is the owner's account (their DID is `did:ixo:<address>`), and
|
|
32
|
+
* the owner's identity in claims messages is the signer's: `agentDid` is
|
|
33
|
+
* `did:ixo:<signer>` (the DID the runtime authenticates to the Hub as) and
|
|
34
|
+
* an evaluation's `agentAddress` is the signer (the chain records the
|
|
35
|
+
* evaluator, and pays it, by that address; Auth Hub requires both). The
|
|
36
|
+
* protocol parses the result; a field the runtime sets (signer, network,
|
|
37
|
+
* chain id) is refused in fixed words, a field from the model's messages
|
|
38
|
+
* in the protocol's (`Invalid messages[0].tokens[0].id`).
|
|
39
|
+
*/
|
|
40
|
+
export function chainTxOperation(input: {
|
|
41
|
+
ownerDid: string;
|
|
42
|
+
chainId: string;
|
|
43
|
+
network: ChainTxOperation['network'];
|
|
44
|
+
memo: string;
|
|
45
|
+
messages: ChainTxHostMessage[];
|
|
46
|
+
}): ActionOperationV1 {
|
|
47
|
+
// Anything but `did:ixo:<address>` leaves no usable signer, which the
|
|
48
|
+
// protocol refuses below.
|
|
49
|
+
const signer = input.ownerDid.startsWith('did:ixo:')
|
|
50
|
+
? input.ownerDid.slice('did:ixo:'.length)
|
|
51
|
+
: '';
|
|
52
|
+
const agentDid = `did:ixo:${signer}`;
|
|
53
|
+
// Identity last: a stray identity field cannot survive.
|
|
54
|
+
const messages: ChainTxMessage[] = input.messages.map((message) =>
|
|
55
|
+
message.type === 'ixo.claims.submit'
|
|
56
|
+
? { ...message, agentDid }
|
|
57
|
+
: message.type === 'ixo.claims.evaluate'
|
|
58
|
+
? { ...message, agentDid, agentAddress: signer }
|
|
59
|
+
: message,
|
|
60
|
+
);
|
|
61
|
+
try {
|
|
62
|
+
return parseOperation({
|
|
63
|
+
kind: 'ixo.chain.tx.v1',
|
|
64
|
+
chainId: input.chainId,
|
|
65
|
+
network: input.network,
|
|
66
|
+
signer,
|
|
67
|
+
memo: input.memo,
|
|
68
|
+
messages,
|
|
69
|
+
});
|
|
70
|
+
} catch (error) {
|
|
71
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
72
|
+
// The signer (the owner's account), the network and the chain id
|
|
73
|
+
// (this oracle's configuration) are the runtime's: no argument changes
|
|
74
|
+
// them.
|
|
75
|
+
if (/^Invalid (signer|network|chainId)$/.test(detail))
|
|
76
|
+
throw new HubActionRefused(
|
|
77
|
+
'refused',
|
|
78
|
+
`This transaction cannot be prepared for this owner and oracle (${detail}: the runtime sets it from the owner's account and this oracle's configuration); retrying or changing the fields will not help.`,
|
|
79
|
+
);
|
|
80
|
+
// The protocol refusing a message built from the model's fields (an
|
|
81
|
+
// address, an amount, an id) is the model's to fix.
|
|
82
|
+
throw new HubActionRefused(
|
|
83
|
+
'refused',
|
|
84
|
+
`The transaction is incomplete (${detail}).`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
10
89
|
export interface PreparedHubActionRequest extends HubActionOrigin {
|
|
11
90
|
provider: string;
|
|
12
91
|
operation: ActionOperationV1;
|
|
@@ -53,6 +132,29 @@ export function assertOperationEnabled(
|
|
|
53
132
|
'refused',
|
|
54
133
|
`This oracle may move at most ${cap} base units of ${operation.denom} per transfer.`,
|
|
55
134
|
);
|
|
135
|
+
} else if (operation.kind === 'ixo.chain.tx.v1') {
|
|
136
|
+
// A pre-check of what the account's capabilities list: its network,
|
|
137
|
+
// the message types and the batch size. The Hub enforces each again
|
|
138
|
+
// (and everything else: signer, addresses, simulation, fee).
|
|
139
|
+
const chainTx = capabilities.chainTx;
|
|
140
|
+
if (!chainTx || capabilities.network !== operation.network)
|
|
141
|
+
throw new HubActionRefused(
|
|
142
|
+
'refused',
|
|
143
|
+
'Chain transactions are not available for this oracle account.',
|
|
144
|
+
);
|
|
145
|
+
if (operation.messages.length > chainTx.maxMessages)
|
|
146
|
+
throw new HubActionRefused(
|
|
147
|
+
'refused',
|
|
148
|
+
`This account allows at most ${chainTx.maxMessages} messages in one transaction.`,
|
|
149
|
+
);
|
|
150
|
+
const unlisted = operation.messages.find(
|
|
151
|
+
(message) => !chainTx.messageTypes.includes(message.type),
|
|
152
|
+
);
|
|
153
|
+
if (unlisted)
|
|
154
|
+
throw new HubActionRefused(
|
|
155
|
+
'refused',
|
|
156
|
+
`This account cannot use ${unlisted.type} messages (it may use ${chainTx.messageTypes.join(', ')}).`,
|
|
157
|
+
);
|
|
56
158
|
} else if (operation.kind === 'resource.read.v1') {
|
|
57
159
|
if (
|
|
58
160
|
operation.ownerDid !== ownerDid ||
|