@agoric/fast-usdc 0.1.1-dev-02967de.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/LICENSE +201 -0
- package/README.md +58 -0
- package/package.json +81 -0
- package/src/cli/bin.js +6 -0
- package/src/cli/cli.js +211 -0
- package/src/cli/config.js +101 -0
- package/src/cli/transfer.js +91 -0
- package/src/constants.js +27 -0
- package/src/exos/README.md +26 -0
- package/src/exos/advancer.js +240 -0
- package/src/exos/liquidity-pool.js +365 -0
- package/src/exos/operator-kit.js +120 -0
- package/src/exos/settler.js +97 -0
- package/src/exos/status-manager.js +176 -0
- package/src/exos/transaction-feed.js +180 -0
- package/src/fast-usdc.contract.js +216 -0
- package/src/fast-usdc.start.js +264 -0
- package/src/pool-share-math.js +189 -0
- package/src/type-guards.js +95 -0
- package/src/types-index.d.ts +1 -0
- package/src/types-index.js +1 -0
- package/src/types.ts +58 -0
- package/src/util/agoric.js +12 -0
- package/src/util/cctp.js +71 -0
- package/src/util/file.js +30 -0
- package/src/util/noble.js +110 -0
- package/src/utils/address.js +71 -0
- package/src/utils/config-marshal.js +130 -0
- package/src/utils/fees.js +58 -0
- package/src/utils/zoe.js +28 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { M } from '@endo/patterns';
|
|
2
|
+
import { makeError, q } from '@endo/errors';
|
|
3
|
+
|
|
4
|
+
import { appendToStoredArray } from '@agoric/store/src/stores/store-utils.js';
|
|
5
|
+
import { CctpTxEvidenceShape, PendingTxShape } from '../type-guards.js';
|
|
6
|
+
import { PendingTxStatus } from '../constants.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @import {MapStore, SetStore} from '@agoric/store';
|
|
10
|
+
* @import {Zone} from '@agoric/zone';
|
|
11
|
+
* @import {CctpTxEvidence, NobleAddress, SeenTxKey, PendingTxKey, PendingTx} from '../types.js';
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Create the key for the pendingTxs MapStore.
|
|
16
|
+
*
|
|
17
|
+
* The key is a composite of `txHash` and `chainId` and not meant to be
|
|
18
|
+
* parsable.
|
|
19
|
+
*
|
|
20
|
+
* @param {NobleAddress} addr
|
|
21
|
+
* @param {bigint} amount
|
|
22
|
+
* @returns {PendingTxKey}
|
|
23
|
+
*/
|
|
24
|
+
const makePendingTxKey = (addr, amount) =>
|
|
25
|
+
`pendingTx:${JSON.stringify([addr, String(amount)])}`;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get the key for the pendingTxs MapStore.
|
|
29
|
+
*
|
|
30
|
+
* @param {CctpTxEvidence} evidence
|
|
31
|
+
* @returns {PendingTxKey}
|
|
32
|
+
*/
|
|
33
|
+
const pendingTxKeyOf = evidence => {
|
|
34
|
+
const { amount, forwardingAddress } = evidence.tx;
|
|
35
|
+
return makePendingTxKey(forwardingAddress, amount);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get the key for the seenTxs SetStore.
|
|
40
|
+
*
|
|
41
|
+
* The key is a composite of `NobleAddress` and transaction `amount` and not
|
|
42
|
+
* meant to be parsable.
|
|
43
|
+
*
|
|
44
|
+
* @param {CctpTxEvidence} evidence
|
|
45
|
+
* @returns {SeenTxKey}
|
|
46
|
+
*/
|
|
47
|
+
const seenTxKeyOf = evidence => {
|
|
48
|
+
const { txHash, chainId } = evidence;
|
|
49
|
+
return `seenTx:${JSON.stringify([txHash, chainId])}`;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The `StatusManager` keeps track of Pending and Seen Transactions
|
|
54
|
+
* via {@link PendingTxStatus} states, aiding in coordination between the `Advancer`
|
|
55
|
+
* and `Settler`.
|
|
56
|
+
*
|
|
57
|
+
* XXX consider separate facets for `Advancing` and `Settling` capabilities.
|
|
58
|
+
*
|
|
59
|
+
* @param {Zone} zone
|
|
60
|
+
*/
|
|
61
|
+
export const prepareStatusManager = zone => {
|
|
62
|
+
/** @type {MapStore<PendingTxKey, PendingTx[]>} */
|
|
63
|
+
const pendingTxs = zone.mapStore('PendingTxs', {
|
|
64
|
+
keyShape: M.string(),
|
|
65
|
+
valueShape: M.arrayOf(PendingTxShape),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
/** @type {SetStore<SeenTxKey>} */
|
|
69
|
+
const seenTxs = zone.setStore('SeenTxs', {
|
|
70
|
+
keyShape: M.string(),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Ensures that `txHash+chainId` has not been processed
|
|
75
|
+
* and adds entry to `seenTxs` set.
|
|
76
|
+
*
|
|
77
|
+
* Also records the CctpTxEvidence and status in `pendingTxs`.
|
|
78
|
+
*
|
|
79
|
+
* @param {CctpTxEvidence} evidence
|
|
80
|
+
* @param {PendingTxStatus} status
|
|
81
|
+
*/
|
|
82
|
+
const recordPendingTx = (evidence, status) => {
|
|
83
|
+
const seenKey = seenTxKeyOf(evidence);
|
|
84
|
+
if (seenTxs.has(seenKey)) {
|
|
85
|
+
throw makeError(`Transaction already seen: ${q(seenKey)}`);
|
|
86
|
+
}
|
|
87
|
+
seenTxs.add(seenKey);
|
|
88
|
+
|
|
89
|
+
appendToStoredArray(
|
|
90
|
+
pendingTxs,
|
|
91
|
+
pendingTxKeyOf(evidence),
|
|
92
|
+
harden({ ...evidence, status }),
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return zone.exo(
|
|
97
|
+
'Fast USDC Status Manager',
|
|
98
|
+
M.interface('StatusManagerI', {
|
|
99
|
+
advance: M.call(CctpTxEvidenceShape).returns(M.undefined()),
|
|
100
|
+
observe: M.call(CctpTxEvidenceShape).returns(M.undefined()),
|
|
101
|
+
hasPendingSettlement: M.call(M.string(), M.bigint()).returns(M.boolean()),
|
|
102
|
+
settle: M.call(M.string(), M.bigint()).returns(M.undefined()),
|
|
103
|
+
lookupPending: M.call(M.string(), M.bigint()).returns(
|
|
104
|
+
M.arrayOf(PendingTxShape),
|
|
105
|
+
),
|
|
106
|
+
}),
|
|
107
|
+
{
|
|
108
|
+
/**
|
|
109
|
+
* Add a new transaction with ADVANCED status
|
|
110
|
+
* @param {CctpTxEvidence} evidence
|
|
111
|
+
*/
|
|
112
|
+
advance(evidence) {
|
|
113
|
+
recordPendingTx(evidence, PendingTxStatus.Advanced);
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Add a new transaction with OBSERVED status
|
|
118
|
+
* @param {CctpTxEvidence} evidence
|
|
119
|
+
*/
|
|
120
|
+
observe(evidence) {
|
|
121
|
+
recordPendingTx(evidence, PendingTxStatus.Observed);
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Find an `ADVANCED` or `OBSERVED` tx waiting to be `SETTLED`
|
|
126
|
+
*
|
|
127
|
+
* @param {NobleAddress} address
|
|
128
|
+
* @param {bigint} amount
|
|
129
|
+
* @returns {boolean}
|
|
130
|
+
*/
|
|
131
|
+
hasPendingSettlement(address, amount) {
|
|
132
|
+
const key = makePendingTxKey(address, amount);
|
|
133
|
+
const pending = pendingTxs.get(key);
|
|
134
|
+
return !!pending.length;
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Mark an `ADVANCED` or `OBSERVED` transaction as `SETTLED` and remove it
|
|
139
|
+
*
|
|
140
|
+
* @param {NobleAddress} address
|
|
141
|
+
* @param {bigint} amount
|
|
142
|
+
*/
|
|
143
|
+
settle(address, amount) {
|
|
144
|
+
const key = makePendingTxKey(address, amount);
|
|
145
|
+
const pending = pendingTxs.get(key);
|
|
146
|
+
|
|
147
|
+
if (!pending.length) {
|
|
148
|
+
throw makeError(`No unsettled entry for ${q(key)}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const pendingCopy = [...pending];
|
|
152
|
+
pendingCopy.shift();
|
|
153
|
+
// TODO, vstorage update for `TxStatus.Settled`
|
|
154
|
+
pendingTxs.set(key, harden(pendingCopy));
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Lookup all pending entries for a given address and amount
|
|
159
|
+
*
|
|
160
|
+
* @param {NobleAddress} address
|
|
161
|
+
* @param {bigint} amount
|
|
162
|
+
* @returns {PendingTx[]}
|
|
163
|
+
*/
|
|
164
|
+
lookupPending(address, amount) {
|
|
165
|
+
const key = makePendingTxKey(address, amount);
|
|
166
|
+
if (!pendingTxs.has(key)) {
|
|
167
|
+
throw makeError(`Key ${q(key)} not yet observed`);
|
|
168
|
+
}
|
|
169
|
+
return pendingTxs.get(key);
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
harden(prepareStatusManager);
|
|
175
|
+
|
|
176
|
+
/** @typedef {ReturnType<typeof prepareStatusManager>} StatusManager */
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { makeTracer } from '@agoric/internal';
|
|
2
|
+
import { prepareDurablePublishKit } from '@agoric/notifier';
|
|
3
|
+
import { M } from '@endo/patterns';
|
|
4
|
+
import { CctpTxEvidenceShape } from '../type-guards.js';
|
|
5
|
+
import { defineInertInvitation } from '../utils/zoe.js';
|
|
6
|
+
import { prepareOperatorKit } from './operator-kit.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @import {Zone} from '@agoric/zone';
|
|
10
|
+
* @import {OperatorKit} from './operator-kit.js';
|
|
11
|
+
* @import {CctpTxEvidence} from '../types.js';
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const trace = makeTracer('TxFeed', true);
|
|
15
|
+
|
|
16
|
+
/** Name in the invitation purse (keyed also by this contract instance) */
|
|
17
|
+
export const INVITATION_MAKERS_DESC = 'oracle operator invitation';
|
|
18
|
+
|
|
19
|
+
const TransactionFeedKitI = harden({
|
|
20
|
+
operatorPowers: M.interface('Transaction Feed Admin', {
|
|
21
|
+
submitEvidence: M.call(CctpTxEvidenceShape, M.any()).returns(),
|
|
22
|
+
}),
|
|
23
|
+
creator: M.interface('Transaction Feed Creator', {
|
|
24
|
+
// TODO narrow the return shape to OperatorKit
|
|
25
|
+
initOperator: M.call(M.string()).returns(M.record()),
|
|
26
|
+
makeOperatorInvitation: M.call(M.string()).returns(M.promise()),
|
|
27
|
+
removeOperator: M.call(M.string()).returns(),
|
|
28
|
+
}),
|
|
29
|
+
public: M.interface('Transaction Feed Public', {
|
|
30
|
+
getEvidenceSubscriber: M.call().returns(M.remotable()),
|
|
31
|
+
}),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {Zone} zone
|
|
36
|
+
* @param {ZCF} zcf
|
|
37
|
+
*/
|
|
38
|
+
export const prepareTransactionFeedKit = (zone, zcf) => {
|
|
39
|
+
const kinds = zone.mapStore('Kinds');
|
|
40
|
+
const makeDurablePublishKit = prepareDurablePublishKit(
|
|
41
|
+
kinds,
|
|
42
|
+
'Transaction Feed',
|
|
43
|
+
);
|
|
44
|
+
/** @type {PublishKit<CctpTxEvidence>} */
|
|
45
|
+
const { publisher, subscriber } = makeDurablePublishKit();
|
|
46
|
+
|
|
47
|
+
const makeInertInvitation = defineInertInvitation(zcf, 'submitting evidence');
|
|
48
|
+
|
|
49
|
+
const makeOperatorKit = prepareOperatorKit(zone, {
|
|
50
|
+
makeInertInvitation,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return zone.exoClassKit(
|
|
54
|
+
'Fast USDC Feed',
|
|
55
|
+
TransactionFeedKitI,
|
|
56
|
+
() => {
|
|
57
|
+
/** @type {MapStore<string, OperatorKit>} */
|
|
58
|
+
const operators = zone.mapStore('operators', {
|
|
59
|
+
durable: true,
|
|
60
|
+
});
|
|
61
|
+
/** @type {MapStore<string, MapStore<string, CctpTxEvidence>>} */
|
|
62
|
+
const pending = zone.mapStore('pending', {
|
|
63
|
+
durable: true,
|
|
64
|
+
});
|
|
65
|
+
return { operators, pending };
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
creator: {
|
|
69
|
+
/**
|
|
70
|
+
* An "operator invitation" is an invitation to be an operator in the
|
|
71
|
+
* oracle netowrk, with the able to submit data to submit evidence of
|
|
72
|
+
* CCTP transactions.
|
|
73
|
+
*
|
|
74
|
+
* @param {string} operatorId unique per contract instance
|
|
75
|
+
* @returns {Promise<Invitation<OperatorKit>>}
|
|
76
|
+
*/
|
|
77
|
+
makeOperatorInvitation(operatorId) {
|
|
78
|
+
const { creator } = this.facets;
|
|
79
|
+
trace('makeOperatorInvitation', operatorId);
|
|
80
|
+
|
|
81
|
+
return zcf.makeInvitation(
|
|
82
|
+
/** @type {OfferHandler<OperatorKit>} */
|
|
83
|
+
seat => {
|
|
84
|
+
seat.exit();
|
|
85
|
+
return creator.initOperator(operatorId);
|
|
86
|
+
},
|
|
87
|
+
INVITATION_MAKERS_DESC,
|
|
88
|
+
);
|
|
89
|
+
},
|
|
90
|
+
/** @param {string} operatorId */
|
|
91
|
+
initOperator(operatorId) {
|
|
92
|
+
const { operators, pending } = this.state;
|
|
93
|
+
trace('initOperator', operatorId);
|
|
94
|
+
|
|
95
|
+
const operatorKit = makeOperatorKit(
|
|
96
|
+
operatorId,
|
|
97
|
+
this.facets.operatorPowers,
|
|
98
|
+
);
|
|
99
|
+
operators.init(operatorId, operatorKit);
|
|
100
|
+
pending.init(
|
|
101
|
+
operatorId,
|
|
102
|
+
zone.detached().mapStore('pending evidence'),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
return operatorKit;
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
/** @param {string} operatorId */
|
|
109
|
+
async removeOperator(operatorId) {
|
|
110
|
+
const { operators } = this.state;
|
|
111
|
+
trace('removeOperator', operatorId);
|
|
112
|
+
const operatorKit = operators.get(operatorId);
|
|
113
|
+
operatorKit.admin.disable();
|
|
114
|
+
operators.delete(operatorId);
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
operatorPowers: {
|
|
118
|
+
/**
|
|
119
|
+
* Add evidence from an operator.
|
|
120
|
+
*
|
|
121
|
+
* @param {CctpTxEvidence} evidence
|
|
122
|
+
* @param {OperatorKit} operatorKit
|
|
123
|
+
*/
|
|
124
|
+
submitEvidence(evidence, operatorKit) {
|
|
125
|
+
const { pending } = this.state;
|
|
126
|
+
trace(
|
|
127
|
+
'submitEvidence',
|
|
128
|
+
operatorKit.operator.getStatus().operatorId,
|
|
129
|
+
evidence,
|
|
130
|
+
);
|
|
131
|
+
const { operatorId } = operatorKit.operator.getStatus();
|
|
132
|
+
|
|
133
|
+
// TODO should this verify that the operator is one made by this exo?
|
|
134
|
+
// This doesn't work...
|
|
135
|
+
// operatorKit === operators.get(operatorId) ||
|
|
136
|
+
// Fail`operatorKit mismatch`;
|
|
137
|
+
|
|
138
|
+
// TODO validate that it's a valid for Fast USDC before accepting
|
|
139
|
+
// E.g. that the `recipientAddress` is the FU settlement account and that
|
|
140
|
+
// the EUD is a chain supported by FU.
|
|
141
|
+
const { txHash } = evidence;
|
|
142
|
+
|
|
143
|
+
// accept the evidence
|
|
144
|
+
{
|
|
145
|
+
const pendingStore = pending.get(operatorId);
|
|
146
|
+
if (pendingStore.has(txHash)) {
|
|
147
|
+
trace(`operator ${operatorId} already reported ${txHash}`);
|
|
148
|
+
} else {
|
|
149
|
+
pendingStore.init(txHash, evidence);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// check agreement
|
|
154
|
+
const found = [...pending.values()].filter(store =>
|
|
155
|
+
store.has(txHash),
|
|
156
|
+
);
|
|
157
|
+
// TODO determine the real policy for checking agreement
|
|
158
|
+
if (found.length < pending.getSize()) {
|
|
159
|
+
// not all have seen it
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// TODO verify that all found deep equal
|
|
164
|
+
|
|
165
|
+
// all agree, so remove from pending and publish
|
|
166
|
+
for (const pendingStore of pending.values()) {
|
|
167
|
+
pendingStore.delete(txHash);
|
|
168
|
+
}
|
|
169
|
+
publisher.publish(evidence);
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
public: {
|
|
173
|
+
getEvidenceSubscriber: () => subscriber,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
);
|
|
177
|
+
};
|
|
178
|
+
harden(prepareTransactionFeedKit);
|
|
179
|
+
|
|
180
|
+
/** @typedef {ReturnType<ReturnType<typeof prepareTransactionFeedKit>>} TransactionFeedKit */
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { AssetKind } from '@agoric/ertp';
|
|
2
|
+
import {
|
|
3
|
+
assertAllDefined,
|
|
4
|
+
deeplyFulfilledObject,
|
|
5
|
+
makeTracer,
|
|
6
|
+
} from '@agoric/internal';
|
|
7
|
+
import { observeIteration, subscribeEach } from '@agoric/notifier';
|
|
8
|
+
import {
|
|
9
|
+
OrchestrationPowersShape,
|
|
10
|
+
withOrchestration,
|
|
11
|
+
} from '@agoric/orchestration';
|
|
12
|
+
import { provideSingleton } from '@agoric/zoe/src/contractSupport/durability.js';
|
|
13
|
+
import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js';
|
|
14
|
+
import { E } from '@endo/far';
|
|
15
|
+
import { M, objectMap } from '@endo/patterns';
|
|
16
|
+
import { depositToSeat } from '@agoric/zoe/src/contractSupport/zoeHelpers.js';
|
|
17
|
+
import { prepareAdvancer } from './exos/advancer.js';
|
|
18
|
+
import { prepareLiquidityPoolKit } from './exos/liquidity-pool.js';
|
|
19
|
+
import { prepareSettler } from './exos/settler.js';
|
|
20
|
+
import { prepareStatusManager } from './exos/status-manager.js';
|
|
21
|
+
import { prepareTransactionFeedKit } from './exos/transaction-feed.js';
|
|
22
|
+
import { defineInertInvitation } from './utils/zoe.js';
|
|
23
|
+
import { FastUSDCTermsShape, FeeConfigShape } from './type-guards.js';
|
|
24
|
+
|
|
25
|
+
const trace = makeTracer('FastUsdc');
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @import {Denom} from '@agoric/orchestration';
|
|
29
|
+
* @import {OrchestrationPowers, OrchestrationTools} from '@agoric/orchestration/src/utils/start-helper.js';
|
|
30
|
+
* @import {Zone} from '@agoric/zone';
|
|
31
|
+
* @import {OperatorKit} from './exos/operator-kit.js';
|
|
32
|
+
* @import {CctpTxEvidence, FeeConfig} from './types.js';
|
|
33
|
+
* @import {RepayAmountKWR, RepayPaymentKWR} from './exos/liquidity-pool.js';
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @typedef {{
|
|
38
|
+
* usdcDenom: Denom;
|
|
39
|
+
* }} FastUsdcTerms
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/** @type {ContractMeta<typeof start>} */
|
|
43
|
+
export const meta = {
|
|
44
|
+
// @ts-expect-error TypedPattern not recognized as record
|
|
45
|
+
customTermsShape: FastUSDCTermsShape,
|
|
46
|
+
privateArgsShape: {
|
|
47
|
+
// @ts-expect-error TypedPattern not recognized as record
|
|
48
|
+
...OrchestrationPowersShape,
|
|
49
|
+
feeConfig: FeeConfigShape,
|
|
50
|
+
marshaller: M.remotable(),
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
harden(meta);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {ZCF<FastUsdcTerms>} zcf
|
|
57
|
+
* @param {OrchestrationPowers & {
|
|
58
|
+
* marshaller: Marshaller;
|
|
59
|
+
* feeConfig: FeeConfig;
|
|
60
|
+
* }} privateArgs
|
|
61
|
+
* @param {Zone} zone
|
|
62
|
+
* @param {OrchestrationTools} tools
|
|
63
|
+
*/
|
|
64
|
+
export const contract = async (zcf, privateArgs, zone, tools) => {
|
|
65
|
+
assert(tools, 'no tools');
|
|
66
|
+
const terms = zcf.getTerms();
|
|
67
|
+
assert('USDC' in terms.brands, 'no USDC brand');
|
|
68
|
+
assert('usdcDenom' in terms, 'no usdcDenom');
|
|
69
|
+
const { feeConfig, marshaller } = privateArgs;
|
|
70
|
+
const { makeRecorderKit } = prepareRecorderKitMakers(
|
|
71
|
+
zone.mapStore('vstorage'),
|
|
72
|
+
marshaller,
|
|
73
|
+
);
|
|
74
|
+
const statusManager = prepareStatusManager(zone);
|
|
75
|
+
const makeSettler = prepareSettler(zone, { statusManager });
|
|
76
|
+
const { chainHub, vowTools } = tools;
|
|
77
|
+
const makeAdvancer = prepareAdvancer(zone, {
|
|
78
|
+
chainHub,
|
|
79
|
+
feeConfig,
|
|
80
|
+
log: trace,
|
|
81
|
+
usdc: harden({
|
|
82
|
+
brand: terms.brands.USDC,
|
|
83
|
+
denom: terms.usdcDenom,
|
|
84
|
+
}),
|
|
85
|
+
statusManager,
|
|
86
|
+
vowTools,
|
|
87
|
+
});
|
|
88
|
+
const makeFeedKit = prepareTransactionFeedKit(zone, zcf);
|
|
89
|
+
assertAllDefined({ makeFeedKit, makeAdvancer, makeSettler, statusManager });
|
|
90
|
+
const feedKit = makeFeedKit();
|
|
91
|
+
const advancer = makeAdvancer(
|
|
92
|
+
// @ts-expect-error FIXME
|
|
93
|
+
{},
|
|
94
|
+
);
|
|
95
|
+
// Connect evidence stream to advancer
|
|
96
|
+
void observeIteration(subscribeEach(feedKit.public.getEvidenceSubscriber()), {
|
|
97
|
+
updateState(evidence) {
|
|
98
|
+
try {
|
|
99
|
+
void advancer.handleTransactionEvent(evidence);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
trace('🚨 Error handling transaction event', err);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
const makeLiquidityPoolKit = prepareLiquidityPoolKit(
|
|
106
|
+
zone,
|
|
107
|
+
zcf,
|
|
108
|
+
terms.brands.USDC,
|
|
109
|
+
{ makeRecorderKit },
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const makeTestInvitation = defineInertInvitation(
|
|
113
|
+
zcf,
|
|
114
|
+
'test of forcing evidence',
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const creatorFacet = zone.exo('Fast USDC Creator', undefined, {
|
|
118
|
+
/** @type {(operatorId: string) => Promise<Invitation<OperatorKit>>} */
|
|
119
|
+
async makeOperatorInvitation(operatorId) {
|
|
120
|
+
return feedKit.creator.makeOperatorInvitation(operatorId);
|
|
121
|
+
},
|
|
122
|
+
/**
|
|
123
|
+
* @param {{ USDC: Amount<'nat'>}} amounts
|
|
124
|
+
*/
|
|
125
|
+
testBorrow(amounts) {
|
|
126
|
+
console.log('🚧🚧 UNTIL: borrow is integrated 🚧🚧', amounts);
|
|
127
|
+
const { zcfSeat: tmpAssetManagerSeat } = zcf.makeEmptySeatKit();
|
|
128
|
+
// eslint-disable-next-line no-use-before-define
|
|
129
|
+
poolKit.borrower.borrow(tmpAssetManagerSeat, amounts);
|
|
130
|
+
return tmpAssetManagerSeat.getCurrentAllocation();
|
|
131
|
+
},
|
|
132
|
+
/**
|
|
133
|
+
*
|
|
134
|
+
* @param {RepayAmountKWR} amounts
|
|
135
|
+
* @param {RepayPaymentKWR} payments
|
|
136
|
+
* @returns {Promise<AmountKeywordRecord>}
|
|
137
|
+
*/
|
|
138
|
+
async testRepay(amounts, payments) {
|
|
139
|
+
console.log('🚧🚧 UNTIL: repay is integrated 🚧🚧', amounts);
|
|
140
|
+
const { zcfSeat: tmpAssetManagerSeat } = zcf.makeEmptySeatKit();
|
|
141
|
+
await depositToSeat(
|
|
142
|
+
zcf,
|
|
143
|
+
tmpAssetManagerSeat,
|
|
144
|
+
await deeplyFulfilledObject(
|
|
145
|
+
objectMap(payments, pmt => E(terms.issuers.USDC).getAmountOf(pmt)),
|
|
146
|
+
),
|
|
147
|
+
payments,
|
|
148
|
+
);
|
|
149
|
+
// eslint-disable-next-line no-use-before-define
|
|
150
|
+
poolKit.repayer.repay(tmpAssetManagerSeat, amounts);
|
|
151
|
+
return tmpAssetManagerSeat.getCurrentAllocation();
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const publicFacet = zone.exo('Fast USDC Public', undefined, {
|
|
156
|
+
// XXX to be removed before production
|
|
157
|
+
/**
|
|
158
|
+
* NB: Any caller with access to this invitation maker has the ability to
|
|
159
|
+
* force handling of evidence.
|
|
160
|
+
*
|
|
161
|
+
* Provide an API call in the form of an invitation maker, so that the
|
|
162
|
+
* capability is available in the smart-wallet bridge during UI testing.
|
|
163
|
+
*
|
|
164
|
+
* @param {CctpTxEvidence} evidence
|
|
165
|
+
*/
|
|
166
|
+
makeTestPushInvitation(evidence) {
|
|
167
|
+
void advancer.handleTransactionEvent(evidence);
|
|
168
|
+
return makeTestInvitation();
|
|
169
|
+
},
|
|
170
|
+
makeDepositInvitation() {
|
|
171
|
+
// eslint-disable-next-line no-use-before-define
|
|
172
|
+
return poolKit.public.makeDepositInvitation();
|
|
173
|
+
},
|
|
174
|
+
makeWithdrawInvitation() {
|
|
175
|
+
// eslint-disable-next-line no-use-before-define
|
|
176
|
+
return poolKit.public.makeWithdrawInvitation();
|
|
177
|
+
},
|
|
178
|
+
getPublicTopics() {
|
|
179
|
+
// eslint-disable-next-line no-use-before-define
|
|
180
|
+
return poolKit.public.getPublicTopics();
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// ^^^ Define all kinds above this line. Keep remote calls below. vvv
|
|
185
|
+
|
|
186
|
+
// NOTE: Using a ZCFMint is helpful for the usual reasons (
|
|
187
|
+
// synchronous mint/burn, keeping assets out of contract vats, ...).
|
|
188
|
+
// And there's just one pool, which suggests building it with zone.exo().
|
|
189
|
+
//
|
|
190
|
+
// But zone.exo() defines a kind and
|
|
191
|
+
// all kinds have to be defined before any remote calls,
|
|
192
|
+
// such as the one to the zoe vat as part of making a ZCFMint.
|
|
193
|
+
//
|
|
194
|
+
// So we use zone.exoClassKit above to define the liquidity pool kind
|
|
195
|
+
// and pass the shareMint into the maker / init function.
|
|
196
|
+
|
|
197
|
+
const shareMint = await provideSingleton(
|
|
198
|
+
zone.mapStore('mint'),
|
|
199
|
+
'PoolShare',
|
|
200
|
+
() =>
|
|
201
|
+
zcf.makeZCFMint('PoolShares', AssetKind.NAT, {
|
|
202
|
+
decimalPlaces: 6,
|
|
203
|
+
}),
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const poolKit = zone.makeOnce('Liquidity Pool kit', () =>
|
|
207
|
+
makeLiquidityPoolKit(shareMint, privateArgs.storageNode),
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
return harden({ creatorFacet, publicFacet });
|
|
211
|
+
};
|
|
212
|
+
harden(contract);
|
|
213
|
+
|
|
214
|
+
export const start = withOrchestration(contract);
|
|
215
|
+
harden(start);
|
|
216
|
+
/** @typedef {typeof start} FastUsdcSF */
|