@agoric/fast-usdc 0.1.1-dev-1f25ee2.0 → 0.1.1-dev-e5813b9.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 +5 -3
- package/package.json +15 -15
- package/src/constants.js +4 -0
- package/src/exos/advancer.js +11 -5
- package/src/exos/operator-kit.js +15 -9
- package/src/exos/settler.js +1 -0
- package/src/exos/status-manager.js +29 -4
- package/src/exos/transaction-feed.js +54 -18
- package/src/fast-usdc.contract.js +6 -5
- package/src/type-guards.js +17 -1
- package/src/types.ts +10 -0
package/README.md
CHANGED
|
@@ -88,12 +88,14 @@ stateDiagram-v2
|
|
|
88
88
|
|
|
89
89
|
```mermaid
|
|
90
90
|
stateDiagram-v2
|
|
91
|
-
Observed -->
|
|
92
|
-
Observed -->
|
|
91
|
+
Observed --> AdvanceSkipped : Risks identified
|
|
92
|
+
Observed --> Advancing : No risks, can advance
|
|
93
|
+
Observed --> Forwarding : No risks, Mint deposited before advance
|
|
93
94
|
Forwarding --> Forwarded
|
|
94
95
|
Advancing --> Advanced
|
|
95
96
|
Advanced --> Disbursed
|
|
96
|
-
|
|
97
|
+
AdvanceSkipped --> Forwarding : Mint deposited
|
|
98
|
+
AdvanceFailed --> Forwarding : Mint deposited
|
|
97
99
|
Advancing --> AdvanceFailed
|
|
98
100
|
Forwarding --> ForwardFailed
|
|
99
101
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/fast-usdc",
|
|
3
|
-
"version": "0.1.1-dev-
|
|
3
|
+
"version": "0.1.1-dev-e5813b9.0+e5813b9",
|
|
4
4
|
"description": "CLI and library for Fast USDC product",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"lint:eslint": "eslint ."
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@agoric/swingset-liveslots": "0.10.3-dev-
|
|
26
|
-
"@agoric/vats": "0.15.2-dev-
|
|
27
|
-
"@agoric/zone": "0.2.3-dev-
|
|
25
|
+
"@agoric/swingset-liveslots": "0.10.3-dev-e5813b9.0+e5813b9",
|
|
26
|
+
"@agoric/vats": "0.15.2-dev-e5813b9.0+e5813b9",
|
|
27
|
+
"@agoric/zone": "0.2.3-dev-e5813b9.0+e5813b9",
|
|
28
28
|
"@fast-check/ava": "^2.0.1",
|
|
29
29
|
"ava": "^5.3.0",
|
|
30
30
|
"c8": "^10.1.2",
|
|
@@ -32,16 +32,16 @@
|
|
|
32
32
|
"ts-blank-space": "^0.4.4"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@agoric/client-utils": "0.1.1-dev-
|
|
36
|
-
"@agoric/cosmic-proto": "0.4.1-dev-
|
|
37
|
-
"@agoric/ertp": "0.16.3-dev-
|
|
38
|
-
"@agoric/internal": "0.3.3-dev-
|
|
39
|
-
"@agoric/notifier": "0.6.3-dev-
|
|
40
|
-
"@agoric/orchestration": "0.1.1-dev-
|
|
41
|
-
"@agoric/store": "0.9.3-dev-
|
|
42
|
-
"@agoric/vat-data": "0.5.3-dev-
|
|
43
|
-
"@agoric/vow": "0.1.1-dev-
|
|
44
|
-
"@agoric/zoe": "0.26.3-dev-
|
|
35
|
+
"@agoric/client-utils": "0.1.1-dev-e5813b9.0+e5813b9",
|
|
36
|
+
"@agoric/cosmic-proto": "0.4.1-dev-e5813b9.0+e5813b9",
|
|
37
|
+
"@agoric/ertp": "0.16.3-dev-e5813b9.0+e5813b9",
|
|
38
|
+
"@agoric/internal": "0.3.3-dev-e5813b9.0+e5813b9",
|
|
39
|
+
"@agoric/notifier": "0.6.3-dev-e5813b9.0+e5813b9",
|
|
40
|
+
"@agoric/orchestration": "0.1.1-dev-e5813b9.0+e5813b9",
|
|
41
|
+
"@agoric/store": "0.9.3-dev-e5813b9.0+e5813b9",
|
|
42
|
+
"@agoric/vat-data": "0.5.3-dev-e5813b9.0+e5813b9",
|
|
43
|
+
"@agoric/vow": "0.1.1-dev-e5813b9.0+e5813b9",
|
|
44
|
+
"@agoric/zoe": "0.26.3-dev-e5813b9.0+e5813b9",
|
|
45
45
|
"@cosmjs/proto-signing": "^0.32.4",
|
|
46
46
|
"@cosmjs/stargate": "^0.32.4",
|
|
47
47
|
"@endo/base64": "^1.0.9",
|
|
@@ -81,5 +81,5 @@
|
|
|
81
81
|
"publishConfig": {
|
|
82
82
|
"access": "public"
|
|
83
83
|
},
|
|
84
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "e5813b9a1b7aff07f84e2b185351b3283e29e141"
|
|
85
85
|
}
|
package/src/constants.js
CHANGED
|
@@ -12,6 +12,8 @@ export const TxStatus = /** @type {const} */ ({
|
|
|
12
12
|
Advanced: 'ADVANCED',
|
|
13
13
|
/** IBC transfer failed (timed out) */
|
|
14
14
|
AdvanceFailed: 'ADVANCE_FAILED',
|
|
15
|
+
/** Advance skipped and waiting for forward */
|
|
16
|
+
AdvanceSkipped: 'ADVANCE_SKIPPED',
|
|
15
17
|
/** settlement for matching advance received and funds disbursed */
|
|
16
18
|
Disbursed: 'DISBURSED',
|
|
17
19
|
/** fallback: do not collect fees */
|
|
@@ -42,5 +44,7 @@ export const PendingTxStatus = /** @type {const} */ ({
|
|
|
42
44
|
AdvanceFailed: 'ADVANCE_FAILED',
|
|
43
45
|
/** IBC transfer is complete */
|
|
44
46
|
Advanced: 'ADVANCED',
|
|
47
|
+
/** Advance skipped and waiting for forward */
|
|
48
|
+
AdvanceSkipped: 'ADVANCE_SKIPPED',
|
|
45
49
|
});
|
|
46
50
|
harden(PendingTxStatus);
|
package/src/exos/advancer.js
CHANGED
|
@@ -8,9 +8,9 @@ import { E } from '@endo/far';
|
|
|
8
8
|
import { M, mustMatch } from '@endo/patterns';
|
|
9
9
|
import { Fail, q } from '@endo/errors';
|
|
10
10
|
import {
|
|
11
|
-
CctpTxEvidenceShape,
|
|
12
11
|
AddressHookShape,
|
|
13
12
|
EvmHashShape,
|
|
13
|
+
EvidenceWithRiskShape,
|
|
14
14
|
} from '../type-guards.js';
|
|
15
15
|
import { makeFeeTools } from '../utils/fees.js';
|
|
16
16
|
|
|
@@ -22,7 +22,7 @@ import { makeFeeTools } from '../utils/fees.js';
|
|
|
22
22
|
* @import {ZoeTools} from '@agoric/orchestration/src/utils/zoe-tools.js';
|
|
23
23
|
* @import {VowTools} from '@agoric/vow';
|
|
24
24
|
* @import {Zone} from '@agoric/zone';
|
|
25
|
-
* @import {CctpTxEvidence, AddressHook, EvmHash, FeeConfig, LogFn, NobleAddress} from '../types.js';
|
|
25
|
+
* @import {CctpTxEvidence, AddressHook, EvmHash, FeeConfig, LogFn, NobleAddress, EvidenceWithRisk} from '../types.js';
|
|
26
26
|
* @import {StatusManager} from './status-manager.js';
|
|
27
27
|
* @import {LiquidityPoolKit} from './liquidity-pool.js';
|
|
28
28
|
*/
|
|
@@ -55,7 +55,7 @@ const AdvancerVowCtxShape = M.splitRecord(
|
|
|
55
55
|
/** type guards internal to the AdvancerKit */
|
|
56
56
|
const AdvancerKitI = harden({
|
|
57
57
|
advancer: M.interface('AdvancerI', {
|
|
58
|
-
handleTransactionEvent: M.callWhen(
|
|
58
|
+
handleTransactionEvent: M.callWhen(EvidenceWithRiskShape).returns(),
|
|
59
59
|
setIntermediateRecipient: M.call(ChainAddressShape).returns(),
|
|
60
60
|
}),
|
|
61
61
|
depositHandler: M.interface('DepositHandlerI', {
|
|
@@ -137,9 +137,9 @@ export const prepareAdvancerKit = (
|
|
|
137
137
|
* `StatusManager` - so we don't need to concern ourselves with
|
|
138
138
|
* preserving the vow chain for callers.
|
|
139
139
|
*
|
|
140
|
-
* @param {
|
|
140
|
+
* @param {EvidenceWithRisk} evidenceWithRisk
|
|
141
141
|
*/
|
|
142
|
-
async handleTransactionEvent(evidence) {
|
|
142
|
+
async handleTransactionEvent({ evidence, risk }) {
|
|
143
143
|
await null;
|
|
144
144
|
try {
|
|
145
145
|
if (statusManager.hasBeenObserved(evidence)) {
|
|
@@ -147,6 +147,12 @@ export const prepareAdvancerKit = (
|
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
if (risk.risksIdentified?.length) {
|
|
151
|
+
log('risks identified, skipping advance');
|
|
152
|
+
statusManager.skipAdvance(evidence, risk.risksIdentified);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
150
156
|
const { borrowerFacet, poolAccount, settlementAddress } =
|
|
151
157
|
this.state;
|
|
152
158
|
const { recipientAddress } = evidence.aux;
|
package/src/exos/operator-kit.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { makeTracer } from '@agoric/internal';
|
|
2
2
|
import { Fail } from '@endo/errors';
|
|
3
3
|
import { M } from '@endo/patterns';
|
|
4
|
-
import { CctpTxEvidenceShape } from '../type-guards.js';
|
|
4
|
+
import { CctpTxEvidenceShape, RiskAssessmentShape } from '../type-guards.js';
|
|
5
5
|
|
|
6
6
|
const trace = makeTracer('TxOperator');
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @import {Zone} from '@agoric/zone';
|
|
10
|
-
* @import {CctpTxEvidence} from '../types.js';
|
|
10
|
+
* @import {CctpTxEvidence, RiskAssessment} from '../types.js';
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @typedef {object} OperatorPowers
|
|
15
|
-
* @property {(evidence: CctpTxEvidence, operatorId: string) => void} attest
|
|
15
|
+
* @property {(evidence: CctpTxEvidence, riskAssessment: RiskAssessment, operatorId: string) => void} attest
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -31,11 +31,15 @@ const OperatorKitI = {
|
|
|
31
31
|
}),
|
|
32
32
|
|
|
33
33
|
invitationMakers: M.interface('InvitationMakers', {
|
|
34
|
-
SubmitEvidence: M.call(CctpTxEvidenceShape)
|
|
34
|
+
SubmitEvidence: M.call(CctpTxEvidenceShape)
|
|
35
|
+
.optional(RiskAssessmentShape)
|
|
36
|
+
.returns(M.promise()),
|
|
35
37
|
}),
|
|
36
38
|
|
|
37
39
|
operator: M.interface('Operator', {
|
|
38
|
-
submitEvidence: M.call(CctpTxEvidenceShape)
|
|
40
|
+
submitEvidence: M.call(CctpTxEvidenceShape)
|
|
41
|
+
.optional(RiskAssessmentShape)
|
|
42
|
+
.returns(),
|
|
39
43
|
getStatus: M.call().returns(M.record()),
|
|
40
44
|
}),
|
|
41
45
|
};
|
|
@@ -81,13 +85,14 @@ export const prepareOperatorKit = (zone, staticPowers) =>
|
|
|
81
85
|
* fluxAggregator contract used for price oracles.
|
|
82
86
|
*
|
|
83
87
|
* @param {CctpTxEvidence} evidence
|
|
88
|
+
* @param {RiskAssessment} [riskAssessment]
|
|
84
89
|
* @returns {Promise<Invitation>}
|
|
85
90
|
*/
|
|
86
|
-
async SubmitEvidence(evidence) {
|
|
91
|
+
async SubmitEvidence(evidence, riskAssessment) {
|
|
87
92
|
const { operator } = this.facets;
|
|
88
93
|
// TODO(bootstrap integration): cause this call to throw and confirm that it
|
|
89
94
|
// shows up in the the smart-wallet UpdateRecord `error` property
|
|
90
|
-
operator.submitEvidence(evidence);
|
|
95
|
+
operator.submitEvidence(evidence, riskAssessment);
|
|
91
96
|
return staticPowers.makeInertInvitation(
|
|
92
97
|
'evidence was pushed in the invitation maker call',
|
|
93
98
|
);
|
|
@@ -98,12 +103,13 @@ export const prepareOperatorKit = (zone, staticPowers) =>
|
|
|
98
103
|
* submit evidence from this operator
|
|
99
104
|
*
|
|
100
105
|
* @param {CctpTxEvidence} evidence
|
|
106
|
+
* @param {RiskAssessment} [riskAssessment]
|
|
101
107
|
* @returns {void}
|
|
102
108
|
*/
|
|
103
|
-
submitEvidence(evidence) {
|
|
109
|
+
submitEvidence(evidence, riskAssessment = {}) {
|
|
104
110
|
const { state } = this;
|
|
105
111
|
!state.disabled || Fail`submitEvidence for disabled operator`;
|
|
106
|
-
state.powers.attest(evidence, state.operatorId);
|
|
112
|
+
state.powers.attest(evidence, riskAssessment, state.operatorId);
|
|
107
113
|
},
|
|
108
114
|
/** @returns {OperatorStatus} */
|
|
109
115
|
getStatus() {
|
package/src/exos/settler.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
/**
|
|
15
15
|
* @import {MapStore, SetStore} from '@agoric/store';
|
|
16
16
|
* @import {Zone} from '@agoric/zone';
|
|
17
|
-
* @import {CctpTxEvidence, NobleAddress, PendingTx, EvmHash, LogFn, TransactionRecord} from '../types.js';
|
|
17
|
+
* @import {CctpTxEvidence, NobleAddress, PendingTx, EvmHash, LogFn, TransactionRecord, EvidenceWithRisk, RiskAssessment} from '../types.js';
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -146,8 +146,9 @@ export const prepareStatusManager = (
|
|
|
146
146
|
*
|
|
147
147
|
* @param {CctpTxEvidence} evidence
|
|
148
148
|
* @param {PendingTxStatus} status
|
|
149
|
+
* @param {string[]} [risksIdentified]
|
|
149
150
|
*/
|
|
150
|
-
const initPendingTx = (evidence, status) => {
|
|
151
|
+
const initPendingTx = (evidence, status, risksIdentified) => {
|
|
151
152
|
const { txHash } = evidence;
|
|
152
153
|
if (seenTxs.has(txHash)) {
|
|
153
154
|
throw makeError(`Transaction already seen: ${q(txHash)}`);
|
|
@@ -160,7 +161,9 @@ export const prepareStatusManager = (
|
|
|
160
161
|
harden({ ...evidence, status }),
|
|
161
162
|
);
|
|
162
163
|
publishEvidence(txHash, evidence);
|
|
163
|
-
if (status
|
|
164
|
+
if (status === PendingTxStatus.AdvanceSkipped) {
|
|
165
|
+
void publishTxnRecord(txHash, harden({ status, risksIdentified }));
|
|
166
|
+
} else if (status !== PendingTxStatus.Observed) {
|
|
164
167
|
// publishEvidence publishes Observed
|
|
165
168
|
void publishTxnRecord(txHash, harden({ status }));
|
|
166
169
|
}
|
|
@@ -194,6 +197,9 @@ export const prepareStatusManager = (
|
|
|
194
197
|
// TODO: naming scheme for transition events
|
|
195
198
|
advance: M.call(CctpTxEvidenceShape).returns(M.undefined()),
|
|
196
199
|
advanceOutcome: M.call(M.string(), M.nat(), M.boolean()).returns(),
|
|
200
|
+
skipAdvance: M.call(CctpTxEvidenceShape, M.arrayOf(M.string())).returns(
|
|
201
|
+
M.undefined(),
|
|
202
|
+
),
|
|
197
203
|
observe: M.call(CctpTxEvidenceShape).returns(M.undefined()),
|
|
198
204
|
hasBeenObserved: M.call(CctpTxEvidenceShape).returns(M.boolean()),
|
|
199
205
|
deleteCompletedTxs: M.call().returns(M.undefined()),
|
|
@@ -203,6 +209,7 @@ export const prepareStatusManager = (
|
|
|
203
209
|
txHash: EvmHashShape,
|
|
204
210
|
status: M.or(
|
|
205
211
|
PendingTxStatus.Advanced,
|
|
212
|
+
PendingTxStatus.AdvanceSkipped,
|
|
206
213
|
PendingTxStatus.AdvanceFailed,
|
|
207
214
|
PendingTxStatus.Observed,
|
|
208
215
|
),
|
|
@@ -224,7 +231,8 @@ export const prepareStatusManager = (
|
|
|
224
231
|
/**
|
|
225
232
|
* Add a new transaction with ADVANCING status
|
|
226
233
|
*
|
|
227
|
-
* NB: this acts like observe() but
|
|
234
|
+
* NB: this acts like observe() but subsequently records an ADVANCING
|
|
235
|
+
* state
|
|
228
236
|
*
|
|
229
237
|
* @param {CctpTxEvidence} evidence
|
|
230
238
|
*/
|
|
@@ -232,6 +240,23 @@ export const prepareStatusManager = (
|
|
|
232
240
|
initPendingTx(evidence, PendingTxStatus.Advancing);
|
|
233
241
|
},
|
|
234
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Add a new transaction with ADVANCE_SKIPPED status
|
|
245
|
+
*
|
|
246
|
+
* NB: this acts like observe() but subsequently records an
|
|
247
|
+
* ADVANCE_SKIPPED state along with risks identified
|
|
248
|
+
*
|
|
249
|
+
* @param {CctpTxEvidence} evidence
|
|
250
|
+
* @param {string[]} risksIdentified
|
|
251
|
+
*/
|
|
252
|
+
skipAdvance(evidence, risksIdentified) {
|
|
253
|
+
initPendingTx(
|
|
254
|
+
evidence,
|
|
255
|
+
PendingTxStatus.AdvanceSkipped,
|
|
256
|
+
risksIdentified,
|
|
257
|
+
);
|
|
258
|
+
},
|
|
259
|
+
|
|
235
260
|
/**
|
|
236
261
|
* Record result of ADVANCING
|
|
237
262
|
*
|
|
@@ -2,14 +2,15 @@ import { makeTracer } from '@agoric/internal';
|
|
|
2
2
|
import { prepareDurablePublishKit } from '@agoric/notifier';
|
|
3
3
|
import { keyEQ, M } from '@endo/patterns';
|
|
4
4
|
import { Fail } from '@endo/errors';
|
|
5
|
-
import { CctpTxEvidenceShape } from '../type-guards.js';
|
|
5
|
+
import { CctpTxEvidenceShape, RiskAssessmentShape } from '../type-guards.js';
|
|
6
6
|
import { defineInertInvitation } from '../utils/zoe.js';
|
|
7
7
|
import { prepareOperatorKit } from './operator-kit.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @import {Zone} from '@agoric/zone';
|
|
11
|
+
* @import {MapStore} from '@agoric/store';
|
|
11
12
|
* @import {OperatorKit} from './operator-kit.js';
|
|
12
|
-
* @import {CctpTxEvidence} from '../types.js';
|
|
13
|
+
* @import {CctpTxEvidence, EvidenceWithRisk, RiskAssessment} from '../types.js';
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
const trace = makeTracer('TxFeed', true);
|
|
@@ -19,7 +20,11 @@ export const INVITATION_MAKERS_DESC = 'oracle operator invitation';
|
|
|
19
20
|
|
|
20
21
|
const TransactionFeedKitI = harden({
|
|
21
22
|
operatorPowers: M.interface('Transaction Feed Admin', {
|
|
22
|
-
attest: M.call(
|
|
23
|
+
attest: M.call(
|
|
24
|
+
CctpTxEvidenceShape,
|
|
25
|
+
RiskAssessmentShape,
|
|
26
|
+
M.string(),
|
|
27
|
+
).returns(),
|
|
23
28
|
}),
|
|
24
29
|
creator: M.interface('Transaction Feed Creator', {
|
|
25
30
|
// TODO narrow the return shape to OperatorKit
|
|
@@ -32,6 +37,22 @@ const TransactionFeedKitI = harden({
|
|
|
32
37
|
}),
|
|
33
38
|
});
|
|
34
39
|
|
|
40
|
+
/**
|
|
41
|
+
* @param {MapStore<string, RiskAssessment>[]} riskStores
|
|
42
|
+
* @param {string} txHash
|
|
43
|
+
*/
|
|
44
|
+
const allRisksIdentified = (riskStores, txHash) => {
|
|
45
|
+
/** @type {Set<string>} */
|
|
46
|
+
const setOfRisks = new Set();
|
|
47
|
+
for (const store of riskStores) {
|
|
48
|
+
const next = store.get(txHash);
|
|
49
|
+
for (const risk of next.risksIdentified ?? []) {
|
|
50
|
+
setOfRisks.add(risk);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return [...setOfRisks.values()].sort();
|
|
54
|
+
};
|
|
55
|
+
|
|
35
56
|
/**
|
|
36
57
|
* @param {Zone} zone
|
|
37
58
|
* @param {ZCF} zcf
|
|
@@ -42,7 +63,7 @@ export const prepareTransactionFeedKit = (zone, zcf) => {
|
|
|
42
63
|
kinds,
|
|
43
64
|
'Transaction Feed',
|
|
44
65
|
);
|
|
45
|
-
/** @type {PublishKit<
|
|
66
|
+
/** @type {PublishKit<EvidenceWithRisk>} */
|
|
46
67
|
const { publisher, subscriber } = makeDurablePublishKit();
|
|
47
68
|
|
|
48
69
|
const makeInertInvitation = defineInertInvitation(zcf, 'submitting evidence');
|
|
@@ -56,14 +77,12 @@ export const prepareTransactionFeedKit = (zone, zcf) => {
|
|
|
56
77
|
TransactionFeedKitI,
|
|
57
78
|
() => {
|
|
58
79
|
/** @type {MapStore<string, OperatorKit>} */
|
|
59
|
-
const operators = zone.mapStore('operators'
|
|
60
|
-
durable: true,
|
|
61
|
-
});
|
|
80
|
+
const operators = zone.mapStore('operators');
|
|
62
81
|
/** @type {MapStore<string, MapStore<string, CctpTxEvidence>>} */
|
|
63
|
-
const pending = zone.mapStore('pending'
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return { operators, pending };
|
|
82
|
+
const pending = zone.mapStore('pending');
|
|
83
|
+
/** @type {MapStore<string, MapStore<string, RiskAssessment>>} */
|
|
84
|
+
const risks = zone.mapStore('risks');
|
|
85
|
+
return { operators, pending, risks };
|
|
67
86
|
},
|
|
68
87
|
{
|
|
69
88
|
creator: {
|
|
@@ -90,7 +109,7 @@ export const prepareTransactionFeedKit = (zone, zcf) => {
|
|
|
90
109
|
},
|
|
91
110
|
/** @param {string} operatorId */
|
|
92
111
|
initOperator(operatorId) {
|
|
93
|
-
const { operators, pending } = this.state;
|
|
112
|
+
const { operators, pending, risks } = this.state;
|
|
94
113
|
trace('initOperator', operatorId);
|
|
95
114
|
|
|
96
115
|
const operatorKit = makeOperatorKit(
|
|
@@ -102,6 +121,7 @@ export const prepareTransactionFeedKit = (zone, zcf) => {
|
|
|
102
121
|
operatorId,
|
|
103
122
|
zone.detached().mapStore('pending evidence'),
|
|
104
123
|
);
|
|
124
|
+
risks.init(operatorId, zone.detached().mapStore('risk assessments'));
|
|
105
125
|
|
|
106
126
|
return operatorKit;
|
|
107
127
|
},
|
|
@@ -122,11 +142,12 @@ export const prepareTransactionFeedKit = (zone, zcf) => {
|
|
|
122
142
|
* NB: the operatorKit is responsible for
|
|
123
143
|
*
|
|
124
144
|
* @param {CctpTxEvidence} evidence
|
|
145
|
+
* @param {RiskAssessment} riskAssessment
|
|
125
146
|
* @param {string} operatorId
|
|
126
147
|
*/
|
|
127
|
-
attest(evidence, operatorId) {
|
|
128
|
-
const { operators, pending } = this.state;
|
|
129
|
-
trace('
|
|
148
|
+
attest(evidence, riskAssessment, operatorId) {
|
|
149
|
+
const { operators, pending, risks } = this.state;
|
|
150
|
+
trace('attest', operatorId, evidence);
|
|
130
151
|
|
|
131
152
|
// TODO https://github.com/Agoric/agoric-sdk/pull/10720
|
|
132
153
|
// TODO validate that it's a valid for Fast USDC before accepting
|
|
@@ -141,6 +162,9 @@ export const prepareTransactionFeedKit = (zone, zcf) => {
|
|
|
141
162
|
trace(`operator ${operatorId} already reported ${txHash}`);
|
|
142
163
|
} else {
|
|
143
164
|
pendingStore.init(txHash, evidence);
|
|
165
|
+
// accept the risk assessment as well
|
|
166
|
+
const riskStore = risks.get(operatorId);
|
|
167
|
+
riskStore.init(txHash, riskAssessment);
|
|
144
168
|
}
|
|
145
169
|
}
|
|
146
170
|
|
|
@@ -183,12 +207,24 @@ export const prepareTransactionFeedKit = (zone, zcf) => {
|
|
|
183
207
|
lastEvidence = next;
|
|
184
208
|
}
|
|
185
209
|
|
|
186
|
-
|
|
210
|
+
const riskStores = [...risks.values()].filter(store =>
|
|
211
|
+
store.has(txHash),
|
|
212
|
+
);
|
|
213
|
+
// take the union of risks identified from all operators
|
|
214
|
+
const risksIdentified = allRisksIdentified(riskStores, txHash);
|
|
215
|
+
|
|
216
|
+
// sufficient agreement, so remove from pending risks, then publish
|
|
187
217
|
for (const store of found) {
|
|
188
218
|
store.delete(txHash);
|
|
189
219
|
}
|
|
190
|
-
|
|
191
|
-
|
|
220
|
+
for (const store of riskStores) {
|
|
221
|
+
store.delete(txHash);
|
|
222
|
+
}
|
|
223
|
+
trace('publishing evidence', evidence, risksIdentified);
|
|
224
|
+
publisher.publish({
|
|
225
|
+
evidence,
|
|
226
|
+
risk: { risksIdentified },
|
|
227
|
+
});
|
|
192
228
|
},
|
|
193
229
|
},
|
|
194
230
|
public: {
|
|
@@ -38,7 +38,7 @@ const ADDRESSES_BAGGAGE_KEY = 'addresses';
|
|
|
38
38
|
* @import {Marshaller, StorageNode} from '@agoric/internal/src/lib-chainStorage.js'
|
|
39
39
|
* @import {Zone} from '@agoric/zone';
|
|
40
40
|
* @import {OperatorKit} from './exos/operator-kit.js';
|
|
41
|
-
* @import {CctpTxEvidence, FeeConfig} from './types.js';
|
|
41
|
+
* @import {CctpTxEvidence, FeeConfig, RiskAssessment} from './types.js';
|
|
42
42
|
*/
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -202,9 +202,10 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
|
|
|
202
202
|
* capability is available in the smart-wallet bridge during UI testing.
|
|
203
203
|
*
|
|
204
204
|
* @param {CctpTxEvidence} evidence
|
|
205
|
+
* @param {RiskAssessment} [risk]
|
|
205
206
|
*/
|
|
206
|
-
makeTestPushInvitation(evidence) {
|
|
207
|
-
void advancer.handleTransactionEvent(evidence);
|
|
207
|
+
makeTestPushInvitation(evidence, risk = {}) {
|
|
208
|
+
void advancer.handleTransactionEvent({ evidence, risk });
|
|
208
209
|
return makeTestInvitation();
|
|
209
210
|
},
|
|
210
211
|
makeDepositInvitation() {
|
|
@@ -303,9 +304,9 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
|
|
|
303
304
|
);
|
|
304
305
|
// Connect evidence stream to advancer
|
|
305
306
|
void observeIteration(subscribeEach(feedKit.public.getEvidenceSubscriber()), {
|
|
306
|
-
updateState(
|
|
307
|
+
updateState(evidenceWithRisk) {
|
|
307
308
|
try {
|
|
308
|
-
void advancer.handleTransactionEvent(
|
|
309
|
+
void advancer.handleTransactionEvent(evidenceWithRisk);
|
|
309
310
|
} catch (err) {
|
|
310
311
|
trace('🚨 Error handling transaction event', err);
|
|
311
312
|
}
|
package/src/type-guards.js
CHANGED
|
@@ -6,7 +6,7 @@ import { PendingTxStatus } from './constants.js';
|
|
|
6
6
|
* @import {TypedPattern} from '@agoric/internal';
|
|
7
7
|
* @import {FastUsdcTerms} from './fast-usdc.contract.js';
|
|
8
8
|
* @import {USDCProposalShapes} from './pool-share-math.js';
|
|
9
|
-
* @import {CctpTxEvidence, FeeConfig, PendingTx, PoolMetrics, ChainPolicy, FeedPolicy, AddressHook, EvmAddress, EvmHash} from './types.js';
|
|
9
|
+
* @import {CctpTxEvidence, FeeConfig, PendingTx, PoolMetrics, ChainPolicy, FeedPolicy, AddressHook, EvmAddress, EvmHash, RiskAssessment, EvidenceWithRisk} from './types.js';
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -49,6 +49,15 @@ export const EvmHashShape = M.string({
|
|
|
49
49
|
});
|
|
50
50
|
harden(EvmHashShape);
|
|
51
51
|
|
|
52
|
+
/** @type {TypedPattern<RiskAssessment>} */
|
|
53
|
+
export const RiskAssessmentShape = M.splitRecord(
|
|
54
|
+
{},
|
|
55
|
+
{
|
|
56
|
+
risksIdentified: M.arrayOf(M.string()),
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
harden(RiskAssessmentShape);
|
|
60
|
+
|
|
52
61
|
/** @type {TypedPattern<CctpTxEvidence>} */
|
|
53
62
|
export const CctpTxEvidenceShape = {
|
|
54
63
|
aux: {
|
|
@@ -67,6 +76,13 @@ export const CctpTxEvidenceShape = {
|
|
|
67
76
|
};
|
|
68
77
|
harden(CctpTxEvidenceShape);
|
|
69
78
|
|
|
79
|
+
/** @type {TypedPattern<EvidenceWithRisk>} */
|
|
80
|
+
export const EvidenceWithRiskShape = {
|
|
81
|
+
evidence: CctpTxEvidenceShape,
|
|
82
|
+
risk: RiskAssessmentShape,
|
|
83
|
+
};
|
|
84
|
+
harden(EvidenceWithRiskShape);
|
|
85
|
+
|
|
70
86
|
/** @type {TypedPattern<PendingTx>} */
|
|
71
87
|
// @ts-expect-error TypedPattern not recognized as record
|
|
72
88
|
export const PendingTxShape = {
|
package/src/types.ts
CHANGED
|
@@ -17,6 +17,10 @@ export type NobleAddress = `noble1${string}`;
|
|
|
17
17
|
export type EvmChainID = number;
|
|
18
18
|
export type EvmChainName = string;
|
|
19
19
|
|
|
20
|
+
export interface RiskAssessment {
|
|
21
|
+
risksIdentified?: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
export interface CctpTxEvidence {
|
|
21
25
|
/** from Noble RPC */
|
|
22
26
|
aux: {
|
|
@@ -35,6 +39,11 @@ export interface CctpTxEvidence {
|
|
|
35
39
|
txHash: EvmHash;
|
|
36
40
|
}
|
|
37
41
|
|
|
42
|
+
export interface EvidenceWithRisk {
|
|
43
|
+
evidence: CctpTxEvidence;
|
|
44
|
+
risk: RiskAssessment;
|
|
45
|
+
}
|
|
46
|
+
|
|
38
47
|
/**
|
|
39
48
|
* 'evidence' only available when it's first observed and not in subsequent
|
|
40
49
|
* updates.
|
|
@@ -42,6 +51,7 @@ export interface CctpTxEvidence {
|
|
|
42
51
|
export interface TransactionRecord extends CopyRecord {
|
|
43
52
|
evidence?: CctpTxEvidence;
|
|
44
53
|
split?: RepayAmountKWR;
|
|
54
|
+
risksIdentified?: string[];
|
|
45
55
|
status: TxStatus;
|
|
46
56
|
}
|
|
47
57
|
|