@agoric/inter-protocol 0.13.3 → 0.13.4-dev-d32a66a.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 +26 -20
- package/bundles/bundle-auctioneer-js-meta.json +722 -0
- package/bundles/bundle-auctioneer.js +1 -0
- package/bundles/bundle-econCommitteeCharter-js-meta.json +514 -0
- package/bundles/bundle-econCommitteeCharter.js +1 -0
- package/bundles/bundle-feeDistributor-js-meta.json +458 -0
- package/bundles/bundle-feeDistributor.js +1 -1
- package/bundles/bundle-fluxAggregatorKit-js-meta.json +674 -0
- package/bundles/bundle-fluxAggregatorKit.js +1 -0
- package/bundles/bundle-psm-js-meta.json +666 -0
- package/bundles/bundle-psm.js +1 -1
- package/bundles/bundle-reserve-js-meta.json +662 -0
- package/bundles/bundle-reserve.js +1 -1
- package/bundles/bundle-scaledPriceAuthority-js-meta.json +542 -0
- package/bundles/bundle-scaledPriceAuthority.js +1 -1
- package/bundles/bundle-vaultFactory-js-meta.json +786 -0
- package/bundles/bundle-vaultFactory.js +1 -1
- package/exported.js +0 -2
- package/package.json +34 -31
- package/scripts/add-collateral-core.js +14 -4
- package/scripts/build-bundles.js +9 -2
- package/scripts/deploy-contracts.js +1 -4
- package/scripts/init-core.js +20 -38
- package/scripts/invite-committee-core.js +1 -1
- package/scripts/manual-price-feed.js +2 -2
- package/scripts/price-feed-core.js +7 -6
- package/scripts/start-local-chain.sh +84 -0
- package/src/auction/auctionBook.js +755 -0
- package/src/auction/auctioneer.js +720 -0
- package/src/auction/offerBook.js +221 -0
- package/src/auction/params.js +174 -0
- package/src/auction/scheduleMath.js +167 -0
- package/src/auction/scheduler.js +370 -0
- package/src/auction/sortedOffers.js +136 -0
- package/src/auction/util.js +55 -0
- package/src/clientSupport.js +324 -0
- package/src/collectFees.js +8 -14
- package/src/contractSupport.js +24 -68
- package/src/econCommitteeCharter.js +161 -77
- package/src/feeDistributor.js +60 -20
- package/src/index.js +2 -0
- package/src/interest-math.js +1 -4
- package/src/interest.js +20 -13
- package/src/price/README.md +57 -0
- package/src/price/fluxAggregatorContract.js +190 -0
- package/src/price/fluxAggregatorKit.js +375 -0
- package/src/price/priceOracleKit.js +127 -0
- package/src/price/roundsManager.js +726 -0
- package/src/proposals/addAssetToVault.js +89 -94
- package/src/proposals/committee-proposal.js +141 -40
- package/src/proposals/core-proposal.js +100 -167
- package/src/proposals/econ-behaviors.js +320 -564
- package/src/proposals/price-feed-proposal.js +175 -78
- package/src/proposals/startEconCommittee.js +25 -12
- package/src/proposals/startPSM.js +231 -211
- package/src/proposals/utils.js +31 -17
- package/src/psm/README.md +71 -0
- package/src/psm/psm.js +272 -160
- package/src/psm/types.js +2 -0
- package/src/reserve/assetReserve.js +56 -413
- package/src/reserve/assetReserveKit.js +303 -0
- package/src/reserve/doc.md +1 -3
- package/src/reserve/params.js +2 -5
- package/src/typeGuards.js +13 -0
- package/src/vaultFactory/Liquidation.md +100 -0
- package/src/vaultFactory/burn.js +2 -1
- package/src/vaultFactory/liquidation.js +270 -108
- package/src/vaultFactory/math.js +95 -0
- package/src/vaultFactory/orderedVaultStore.js +0 -1
- package/src/vaultFactory/params.js +133 -112
- package/src/vaultFactory/prioritizedVaults.js +36 -79
- package/src/vaultFactory/storeUtils.js +67 -9
- package/src/vaultFactory/type-imports.js +2 -0
- package/src/vaultFactory/types.js +22 -26
- package/src/vaultFactory/vault.js +772 -709
- package/src/vaultFactory/vaultDirector.js +480 -483
- package/src/vaultFactory/vaultFactory.js +99 -60
- package/src/vaultFactory/vaultHolder.js +121 -95
- package/src/vaultFactory/vaultKit.js +39 -31
- package/src/vaultFactory/vaultManager.js +1249 -868
- package/bundles/bundle-amm-meta.js +0 -618
- package/bundles/bundle-amm.js +0 -1
- package/bundles/bundle-feeDistributor-meta.js +0 -346
- package/bundles/bundle-interchainPool-meta.js +0 -534
- package/bundles/bundle-interchainPool.js +0 -1
- package/bundles/bundle-liquidateIncrementally-meta.js +0 -442
- package/bundles/bundle-liquidateIncrementally.js +0 -1
- package/bundles/bundle-liquidateMinimum-meta.js +0 -442
- package/bundles/bundle-liquidateMinimum.js +0 -1
- package/bundles/bundle-psm-meta.js +0 -538
- package/bundles/bundle-psmCharter-meta.js +0 -418
- package/bundles/bundle-psmCharter.js +0 -1
- package/bundles/bundle-reserve-meta.js +0 -542
- package/bundles/bundle-scaledPriceAuthority-meta.js +0 -442
- package/bundles/bundle-stakeFactory-meta.js +0 -574
- package/bundles/bundle-stakeFactory.js +0 -1
- package/bundles/bundle-vaultFactory-meta.js +0 -598
- package/scripts/add-initial-liquidity.js +0 -180
- package/src/collect.js +0 -13
- package/src/interchainPool.js +0 -135
- package/src/makeTracer.js +0 -36
- package/src/my-lien.js +0 -85
- package/src/proposals/demoIssuers.js +0 -685
- package/src/proposals/sim-behaviors.js +0 -40
- package/src/psm/psmCharter.js +0 -139
- package/src/stakeFactory/README.md +0 -62
- package/src/stakeFactory/attestation.js +0 -267
- package/src/stakeFactory/attestationTool.js +0 -66
- package/src/stakeFactory/constants.js +0 -16
- package/src/stakeFactory/params.js +0 -31
- package/src/stakeFactory/stakeFactory.js +0 -236
- package/src/stakeFactory/stakeFactoryKit.js +0 -455
- package/src/stakeFactory/stakeFactoryManager.js +0 -296
- package/src/stakeFactory/types.js +0 -42
- package/src/vaultFactory/liquidateIncrementally.js +0 -350
- package/src/vaultFactory/liquidateMinimum.js +0 -100
- package/src/vpool-xyk-amm/AttackersGuide.md +0 -89
- package/src/vpool-xyk-amm/addLiquidity.js +0 -222
- package/src/vpool-xyk-amm/addPool.js +0 -197
- package/src/vpool-xyk-amm/constantProduct/AMM-Trade.jpeg +0 -0
- package/src/vpool-xyk-amm/constantProduct/README.md +0 -204
- package/src/vpool-xyk-amm/constantProduct/calcFees.js +0 -99
- package/src/vpool-xyk-amm/constantProduct/calcSwapPrices.js +0 -85
- package/src/vpool-xyk-amm/constantProduct/core.js +0 -153
- package/src/vpool-xyk-amm/constantProduct/defaults.js +0 -5
- package/src/vpool-xyk-amm/constantProduct/getXY.js +0 -52
- package/src/vpool-xyk-amm/constantProduct/internal-types.js +0 -116
- package/src/vpool-xyk-amm/constantProduct/invariants.js +0 -43
- package/src/vpool-xyk-amm/constantProduct/swap.js +0 -272
- package/src/vpool-xyk-amm/constantProduct/types.js +0 -32
- package/src/vpool-xyk-amm/doublePool.js +0 -194
- package/src/vpool-xyk-amm/estimate.js +0 -155
- package/src/vpool-xyk-amm/multipoolMarketMaker.js +0 -520
- package/src/vpool-xyk-amm/params.js +0 -61
- package/src/vpool-xyk-amm/pool.js +0 -396
- package/src/vpool-xyk-amm/priceAuthority.js +0 -94
- package/src/vpool-xyk-amm/removeLiquidity.js +0 -31
- package/src/vpool-xyk-amm/singlePool.js +0 -73
- package/src/vpool-xyk-amm/swap.js +0 -69
- package/src/vpool-xyk-amm/types.js +0 -154
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
// @jessie-check
|
|
3
|
-
import { AmountMath } from '@agoric/ertp';
|
|
4
|
-
import { handleParamGovernance, ParamTypes } from '@agoric/governance';
|
|
5
|
-
import { E, Far } from '@endo/far';
|
|
6
|
-
import { makeMakeCollectFeesInvitation } from '../collectFees.js';
|
|
7
|
-
import { makeAttestationFacets } from './attestation.js';
|
|
8
|
-
import { ManagerKW as KW } from './constants.js';
|
|
9
|
-
import { makeStakeFactoryKit } from './stakeFactoryKit.js';
|
|
10
|
-
import { makeStakeFactoryManager } from './stakeFactoryManager.js';
|
|
11
|
-
|
|
12
|
-
const { details: X } = assert;
|
|
13
|
-
const { values } = Object;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Provide loans on the basis of staked assets that earn rewards.
|
|
17
|
-
*
|
|
18
|
-
* In addition to brands and issuers for `Staked`, `Minted`, and attestation,
|
|
19
|
-
* terms of the contract include a periodic `InterestRate`
|
|
20
|
-
* plus a `LoanFee` proportional to the amount borrowed, as well as
|
|
21
|
-
* a `MintingRatio` of funds to (mint and) loan per unit of staked asset.
|
|
22
|
-
* These terms are subject to change by the `Electorate`
|
|
23
|
-
* and `electionManager` terms.
|
|
24
|
-
*
|
|
25
|
-
* @typedef {{
|
|
26
|
-
* DebtLimit: 'amount',
|
|
27
|
-
* MintingRatio: 'ratio',
|
|
28
|
-
* InterestRate: 'ratio',
|
|
29
|
-
* LoanFee: 'ratio',
|
|
30
|
-
* }} StakeFactoryParams
|
|
31
|
-
*
|
|
32
|
-
* As in vaultFactory, `timerService` provides the periodic signal to
|
|
33
|
-
* charge interest according to `chargingPeriod` and `recordingPeriod`.
|
|
34
|
-
*
|
|
35
|
-
* @typedef { GovernanceTerms<StakeFactoryParams> & {
|
|
36
|
-
* timerService: TimerService,
|
|
37
|
-
* chargingPeriod: bigint,
|
|
38
|
-
* recordingPeriod: bigint,
|
|
39
|
-
* lienAttestationName?: string,
|
|
40
|
-
* }} StakeFactoryTerms
|
|
41
|
-
*
|
|
42
|
-
* The public facet provides access to invitations to get a loan
|
|
43
|
-
* or to return an attestation in order to release a lien on staked assets.
|
|
44
|
-
*
|
|
45
|
-
* @typedef {{
|
|
46
|
-
* makeLoanInvitation: () => Promise<Invitation>,
|
|
47
|
-
* makeReturnAttInvitation: () => Promise<Invitation>,
|
|
48
|
-
* }} StakeFactoryPublic
|
|
49
|
-
*
|
|
50
|
-
* To take out a loan, get an `AttestationMaker` for your address from
|
|
51
|
-
* the creator of this contract, and use
|
|
52
|
-
* `E(attMaker).makeAttestation(stakedAmount)` to take out a lien
|
|
53
|
-
* and get a payment that attests to the lien. Provide the payment
|
|
54
|
-
* in the `Attestation` keyword of an offer,
|
|
55
|
-
* using `{ want: { Debt: amountWanted }}`.
|
|
56
|
-
*
|
|
57
|
-
* Then, using the invitationMakers pattern, use `AdjustBalances` to
|
|
58
|
-
* pay down the loan or otherwise adjust the `Debt` and `Attestation`.
|
|
59
|
-
*
|
|
60
|
-
* Finally, `Close` the loan, providing `{ give: Debt: debtAmount }}`
|
|
61
|
-
*
|
|
62
|
-
* To start the contract, authorize minting assets by providing `feeMintAccess`
|
|
63
|
-
* and provide access to the underlying staking infrastructure in `lienBridge`.
|
|
64
|
-
*
|
|
65
|
-
* @typedef {{
|
|
66
|
-
* feeMintAccess: FeeMintAccess,
|
|
67
|
-
* initialPoserInvitation: Invitation,
|
|
68
|
-
* lienBridge: ERef<StakingAuthority>,
|
|
69
|
-
* storageNode: StorageNode,
|
|
70
|
-
* marshaller: Marshaller,
|
|
71
|
-
* }} StakeFactoryPrivateArgs
|
|
72
|
-
*
|
|
73
|
-
* The creator facet can make an `AttestationMaker` for each account, which
|
|
74
|
-
* authorizes placing a lien some of the staked assets in that account.
|
|
75
|
-
* @typedef {{
|
|
76
|
-
* provideAttestationMaker: (addr: string) => AttestationTool,
|
|
77
|
-
* makeCollectFeesInvitation: () => Promise<Invitation>,
|
|
78
|
-
* }} StakeFactoryCreator
|
|
79
|
-
*
|
|
80
|
-
* @type {ContractStartFn<StakeFactoryPublic, ERef<GovernedCreatorFacet<StakeFactoryCreator>>,
|
|
81
|
-
* StakeFactoryTerms, StakeFactoryPrivateArgs>}
|
|
82
|
-
*/
|
|
83
|
-
export const start = async (
|
|
84
|
-
zcf,
|
|
85
|
-
{
|
|
86
|
-
feeMintAccess,
|
|
87
|
-
initialPoserInvitation,
|
|
88
|
-
lienBridge,
|
|
89
|
-
storageNode,
|
|
90
|
-
marshaller,
|
|
91
|
-
},
|
|
92
|
-
) => {
|
|
93
|
-
const {
|
|
94
|
-
brands: { Stake: stakeBrand },
|
|
95
|
-
timerService,
|
|
96
|
-
chargingPeriod,
|
|
97
|
-
recordingPeriod,
|
|
98
|
-
} = zcf.getTerms();
|
|
99
|
-
assert.typeof(chargingPeriod, 'bigint', 'chargingPeriod must be a bigint');
|
|
100
|
-
assert.typeof(recordingPeriod, 'bigint', 'recordingPeriod must be a bigint');
|
|
101
|
-
assert(storageNode && marshaller, 'missing storageNode or marshaller');
|
|
102
|
-
|
|
103
|
-
/** @type {ZCFMint<'nat'>} */
|
|
104
|
-
const debtMint = await zcf.registerFeeMint(KW.Debt, feeMintAccess);
|
|
105
|
-
const { brand: debtBrand } = debtMint.getIssuerRecord();
|
|
106
|
-
|
|
107
|
-
const att = await makeAttestationFacets(zcf, stakeBrand, lienBridge);
|
|
108
|
-
const attestBrand = await E(att.publicFacet).getBrand();
|
|
109
|
-
|
|
110
|
-
const { augmentPublicFacet, makeGovernorFacet, params } =
|
|
111
|
-
await handleParamGovernance(
|
|
112
|
-
zcf,
|
|
113
|
-
initialPoserInvitation,
|
|
114
|
-
{
|
|
115
|
-
DebtLimit: ParamTypes.AMOUNT,
|
|
116
|
-
InterestRate: ParamTypes.RATIO,
|
|
117
|
-
LoanFee: ParamTypes.RATIO,
|
|
118
|
-
MintingRatio: ParamTypes.RATIO,
|
|
119
|
-
},
|
|
120
|
-
storageNode,
|
|
121
|
-
marshaller,
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
/** For temporary staging of newly minted tokens */
|
|
125
|
-
const { zcfSeat: mintSeat } = zcf.makeEmptySeatKit();
|
|
126
|
-
const { zcfSeat: rewardPoolSeat } = zcf.makeEmptySeatKit();
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Let the manager add rewards to the rewardPoolSeat
|
|
130
|
-
* without directly exposing the rewardPoolSeat to them.
|
|
131
|
-
*
|
|
132
|
-
* @type {MintAndReallocate}
|
|
133
|
-
*/
|
|
134
|
-
const mintAndReallocate = (toMint, fee, seat, ...otherSeats) => {
|
|
135
|
-
const kept = AmountMath.subtract(toMint, fee);
|
|
136
|
-
debtMint.mintGains(harden({ [KW.Debt]: toMint }), mintSeat);
|
|
137
|
-
try {
|
|
138
|
-
rewardPoolSeat.incrementBy(
|
|
139
|
-
mintSeat.decrementBy(harden({ [KW.Debt]: fee })),
|
|
140
|
-
);
|
|
141
|
-
seat.incrementBy(mintSeat.decrementBy(harden({ [KW.Debt]: kept })));
|
|
142
|
-
zcf.reallocate(
|
|
143
|
-
rewardPoolSeat,
|
|
144
|
-
mintSeat,
|
|
145
|
-
seat,
|
|
146
|
-
...otherSeats.filter(s => s.hasStagedAllocation()),
|
|
147
|
-
);
|
|
148
|
-
} catch (e) {
|
|
149
|
-
mintSeat.clear();
|
|
150
|
-
rewardPoolSeat.clear();
|
|
151
|
-
// Make best efforts to burn the newly minted tokens, for hygiene.
|
|
152
|
-
// That only relies on the internal mint, so it cannot fail without
|
|
153
|
-
// there being much larger problems. There's no risk of tokens being
|
|
154
|
-
// stolen here because the staging for them was already cleared.
|
|
155
|
-
debtMint.burnLosses(harden({ [KW.Debt]: toMint }), mintSeat);
|
|
156
|
-
throw e;
|
|
157
|
-
} finally {
|
|
158
|
-
assert(
|
|
159
|
-
values(mintSeat.getCurrentAllocation()).every(a =>
|
|
160
|
-
AmountMath.isEmpty(a),
|
|
161
|
-
),
|
|
162
|
-
X`Stage should be empty of Debt`,
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
// TODO add aggregate debt tracking at the vaultFactory level #4482
|
|
166
|
-
// totalDebt = AmountMath.add(totalDebt, toMint);
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
const burnDebt = (toBurn, seat) => {
|
|
170
|
-
debtMint.burnLosses(harden({ [KW.Debt]: toBurn }), seat);
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const mintPowers = Far('mintPowers', {
|
|
174
|
-
burnDebt,
|
|
175
|
-
getGovernedParams: () => params, // XXX until governance support is durable
|
|
176
|
-
mintAndReallocate,
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
const startTimeStamp = await E(timerService).getCurrentTimestamp();
|
|
180
|
-
const { manager } = makeStakeFactoryManager(
|
|
181
|
-
zcf,
|
|
182
|
-
debtMint,
|
|
183
|
-
harden({ Attestation: attestBrand, debt: debtBrand, Stake: stakeBrand }),
|
|
184
|
-
mintPowers,
|
|
185
|
-
{ timerService, chargingPeriod, recordingPeriod, startTimeStamp },
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* @param {ZCFSeat} seat
|
|
190
|
-
*/
|
|
191
|
-
const offerHandler = seat => {
|
|
192
|
-
const { helper, pot } = makeStakeFactoryKit(zcf, seat, manager);
|
|
193
|
-
|
|
194
|
-
return harden({
|
|
195
|
-
publicSubscribers: {
|
|
196
|
-
// XXX should come from manager directly https://github.com/Agoric/agoric-sdk/issues/5814
|
|
197
|
-
asset: manager.getAssetSubscriber(),
|
|
198
|
-
vault: pot.getSubscriber(),
|
|
199
|
-
},
|
|
200
|
-
invitationMakers: Far('invitation makers', {
|
|
201
|
-
AdjustBalances: () =>
|
|
202
|
-
zcf.makeInvitation(
|
|
203
|
-
seatx => helper.adjustBalancesHook(seatx),
|
|
204
|
-
'AdjustBalances',
|
|
205
|
-
),
|
|
206
|
-
CloseVault: () =>
|
|
207
|
-
zcf.makeInvitation(seatx => helper.closeHook(seatx), 'CloseVault'),
|
|
208
|
-
}),
|
|
209
|
-
vault: pot,
|
|
210
|
-
});
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const publicFacet = augmentPublicFacet(
|
|
214
|
-
harden({
|
|
215
|
-
makeLoanInvitation: () =>
|
|
216
|
-
zcf.makeInvitation(offerHandler, 'make stakeFactory'),
|
|
217
|
-
makeReturnAttInvitation: att.publicFacet.makeReturnAttInvitation,
|
|
218
|
-
}),
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
/** @type {ERef<StakeFactoryCreator>} */
|
|
222
|
-
const creatorFacet = Far('stakeFactory creator', {
|
|
223
|
-
provideAttestationMaker: att.creatorFacet.provideAttestationTool,
|
|
224
|
-
makeCollectFeesInvitation: () => {
|
|
225
|
-
return makeMakeCollectFeesInvitation(
|
|
226
|
-
zcf,
|
|
227
|
-
rewardPoolSeat,
|
|
228
|
-
debtBrand,
|
|
229
|
-
'Minted',
|
|
230
|
-
).makeCollectFeesInvitation();
|
|
231
|
-
},
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
return { publicFacet, creatorFacet: makeGovernorFacet(creatorFacet) };
|
|
235
|
-
};
|
|
236
|
-
harden(start);
|
|
@@ -1,455 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
// @jessie-check
|
|
3
|
-
import { AmountMath, AssetKind } from '@agoric/ertp';
|
|
4
|
-
import { assertProposalShape } from '@agoric/zoe/src/contractSupport/index.js';
|
|
5
|
-
import { ceilMultiplyBy } from '@agoric/zoe/src/contractSupport/ratio.js';
|
|
6
|
-
import { makePublishKit } from '@agoric/notifier';
|
|
7
|
-
import { bindAllMethods } from '@agoric/internal';
|
|
8
|
-
import { M, matches } from '@agoric/store';
|
|
9
|
-
import { defineKindMulti } from '@agoric/vat-data';
|
|
10
|
-
import { makeTracer } from '../makeTracer.js';
|
|
11
|
-
import { addSubtract, assertOnlyKeys, stageDelta } from '../contractSupport.js';
|
|
12
|
-
import { calculateCurrentDebt, reverseInterest } from '../interest-math.js';
|
|
13
|
-
import { ManagerKW as KW } from './constants.js';
|
|
14
|
-
|
|
15
|
-
const { details: X, quote: q } = assert;
|
|
16
|
-
|
|
17
|
-
const trace = makeTracer('R1');
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Calculate the fee, the amount to mint and the resulting debt.
|
|
21
|
-
*
|
|
22
|
-
* @param {Ratio} feeCoeff fee coefficient
|
|
23
|
-
* @param {Amount} currentDebt
|
|
24
|
-
* @param {Amount} giveAmount
|
|
25
|
-
* @param {Amount} wantAmount
|
|
26
|
-
*/
|
|
27
|
-
const calculateFee = (feeCoeff, currentDebt, giveAmount, wantAmount) => {
|
|
28
|
-
const fee = ceilMultiplyBy(wantAmount, feeCoeff);
|
|
29
|
-
const toMint = AmountMath.add(wantAmount, fee);
|
|
30
|
-
const newDebt = addSubtract(currentDebt, toMint, giveAmount);
|
|
31
|
-
return { newDebt, toMint, fee };
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @typedef {Readonly<{
|
|
36
|
-
* collateralBrand: Brand,
|
|
37
|
-
* debtBrand: Brand,
|
|
38
|
-
* manager: import('./stakeFactoryManager.js').StakeFactoryManager,
|
|
39
|
-
* subscriber: Subscriber<unknown>
|
|
40
|
-
* vaultSeat: ZCFSeat,
|
|
41
|
-
* zcf: ZCF,
|
|
42
|
-
* }>} ImmutableState
|
|
43
|
-
* @typedef {{
|
|
44
|
-
* open: boolean,
|
|
45
|
-
* debtSnapshot: Amount<'nat'>,
|
|
46
|
-
* interestSnapshot: Ratio,
|
|
47
|
-
* publisher: Publisher<unknown> | null,
|
|
48
|
-
* }} MutableState
|
|
49
|
-
* @typedef {MutableState & ImmutableState} State
|
|
50
|
-
* @typedef {{
|
|
51
|
-
* state: State,
|
|
52
|
-
* facets: {
|
|
53
|
-
* helper: import('@agoric/vat-data/src/types').KindFacet<typeof helperBehavior>,
|
|
54
|
-
* pot: import('@agoric/vat-data/src/types').KindFacet<typeof potBehavior>,
|
|
55
|
-
* },
|
|
56
|
-
* }} MethodContext
|
|
57
|
-
*/
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Make stakeFactory kit state
|
|
61
|
-
*
|
|
62
|
-
* @param {ZCF} zcf
|
|
63
|
-
* @param {ZCFSeat} startSeat
|
|
64
|
-
* @param {import('./stakeFactoryManager.js').StakeFactoryManager} manager
|
|
65
|
-
* @returns {State}
|
|
66
|
-
*/
|
|
67
|
-
const initState = (zcf, startSeat, manager) => {
|
|
68
|
-
const collateralBrand = manager.getCollateralBrand();
|
|
69
|
-
const debtBrand = manager.getDebtBrand();
|
|
70
|
-
|
|
71
|
-
const { zcfSeat: vaultSeat } = zcf.makeEmptySeatKit();
|
|
72
|
-
|
|
73
|
-
const emptyDebt = AmountMath.makeEmpty(debtBrand);
|
|
74
|
-
|
|
75
|
-
const initialDebt = (() => {
|
|
76
|
-
assertProposalShape(startSeat, {
|
|
77
|
-
give: { [KW.Attestation]: null },
|
|
78
|
-
want: { [KW.Debt]: null },
|
|
79
|
-
});
|
|
80
|
-
const {
|
|
81
|
-
give: { [KW.Attestation]: attestationGiven },
|
|
82
|
-
want: { [KW.Debt]: runWanted },
|
|
83
|
-
} = startSeat.getProposal();
|
|
84
|
-
|
|
85
|
-
const { maxDebt } = manager.maxDebtForLien(attestationGiven);
|
|
86
|
-
AmountMath.isGTE(maxDebt, runWanted) ||
|
|
87
|
-
assert.fail(
|
|
88
|
-
X`wanted ${runWanted}, more than max debt (${maxDebt}) for ${attestationGiven}`,
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
const { newDebt, fee, toMint } = calculateFee(
|
|
92
|
-
manager.getLoanFee(),
|
|
93
|
-
emptyDebt,
|
|
94
|
-
emptyDebt,
|
|
95
|
-
runWanted,
|
|
96
|
-
);
|
|
97
|
-
!AmountMath.isEmpty(fee) ||
|
|
98
|
-
assert.fail(
|
|
99
|
-
X`loan requested (${runWanted}) is too small; cannot accrue interest`,
|
|
100
|
-
);
|
|
101
|
-
assert(AmountMath.isEqual(newDebt, toMint), X`loan fee mismatch`);
|
|
102
|
-
trace('init', { runWanted, fee, attestationGiven });
|
|
103
|
-
|
|
104
|
-
vaultSeat.incrementBy(
|
|
105
|
-
startSeat.decrementBy(harden({ [KW.Attestation]: attestationGiven })),
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
manager.mintAndReallocate(toMint, fee, startSeat, vaultSeat);
|
|
109
|
-
|
|
110
|
-
startSeat.exit();
|
|
111
|
-
return newDebt;
|
|
112
|
-
})();
|
|
113
|
-
manager.applyDebtDelta(emptyDebt, initialDebt);
|
|
114
|
-
|
|
115
|
-
const { publisher, subscriber } = makePublishKit();
|
|
116
|
-
|
|
117
|
-
/** @type {ImmutableState} */
|
|
118
|
-
const immutable = {
|
|
119
|
-
collateralBrand,
|
|
120
|
-
debtBrand,
|
|
121
|
-
manager,
|
|
122
|
-
subscriber,
|
|
123
|
-
vaultSeat,
|
|
124
|
-
zcf,
|
|
125
|
-
};
|
|
126
|
-
return {
|
|
127
|
-
...immutable,
|
|
128
|
-
open: true,
|
|
129
|
-
// Two values from the same moment
|
|
130
|
-
interestSnapshot: manager.getCompoundedInterest(),
|
|
131
|
-
debtSnapshot: initialDebt,
|
|
132
|
-
publisher,
|
|
133
|
-
};
|
|
134
|
-
};
|
|
135
|
-
const helperBehavior = {
|
|
136
|
-
/**
|
|
137
|
-
* @param {MethodContext} context
|
|
138
|
-
* @param {ZCFSeat} seat
|
|
139
|
-
*/
|
|
140
|
-
getCollateralAllocated: ({ state }, seat) =>
|
|
141
|
-
seat.getAmountAllocated(KW.Attestation, state.collateralBrand),
|
|
142
|
-
/**
|
|
143
|
-
* @param {MethodContext} context
|
|
144
|
-
* @param {ZCFSeat} seat
|
|
145
|
-
*/
|
|
146
|
-
getMintedAllocated: ({ state }, seat) =>
|
|
147
|
-
seat.getAmountAllocated(KW.Debt, state.debtBrand),
|
|
148
|
-
/**
|
|
149
|
-
* @param {MethodContext} context
|
|
150
|
-
*/
|
|
151
|
-
getCollateralAmount: ({ state, facets }) => {
|
|
152
|
-
const { collateralBrand, vaultSeat } = state;
|
|
153
|
-
const { helper } = facets;
|
|
154
|
-
const emptyCollateral = AmountMath.makeEmpty(
|
|
155
|
-
collateralBrand,
|
|
156
|
-
AssetKind.COPY_BAG,
|
|
157
|
-
);
|
|
158
|
-
// getCollateralAllocated would return final allocations
|
|
159
|
-
return vaultSeat.hasExited()
|
|
160
|
-
? emptyCollateral
|
|
161
|
-
: helper.getCollateralAllocated(vaultSeat);
|
|
162
|
-
},
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* @param {MethodContext} context
|
|
166
|
-
* @param {boolean} newActive */
|
|
167
|
-
snapshotState: ({ state, facets }, newActive) => {
|
|
168
|
-
const { debtSnapshot: debt, interestSnapshot: interest } = state;
|
|
169
|
-
const { helper } = facets;
|
|
170
|
-
/** @type {VaultNotification} */
|
|
171
|
-
const result = harden({
|
|
172
|
-
debtSnapshot: { debt, interest },
|
|
173
|
-
locked: helper.getCollateralAmount(),
|
|
174
|
-
// newPhase param is so that makeTransferInvitation can finish without setting the vault's phase
|
|
175
|
-
// TODO refactor https://github.com/Agoric/agoric-sdk/issues/4415
|
|
176
|
-
vaultState: newActive ? 'active' : 'closed',
|
|
177
|
-
});
|
|
178
|
-
return result;
|
|
179
|
-
},
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* call this whenever anything changes!
|
|
183
|
-
*
|
|
184
|
-
* @param {MethodContext} context
|
|
185
|
-
*/
|
|
186
|
-
updateUiState: async ({ state, facets }) => {
|
|
187
|
-
const { open: active, publisher } = state;
|
|
188
|
-
if (!publisher) {
|
|
189
|
-
console.warn('updateUiState called after ui.publisher removed');
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
const uiState = facets.helper.snapshotState(active);
|
|
193
|
-
trace('updateUiState', uiState);
|
|
194
|
-
|
|
195
|
-
if (active) {
|
|
196
|
-
publisher.publish(uiState);
|
|
197
|
-
} else {
|
|
198
|
-
publisher.finish(uiState);
|
|
199
|
-
state.publisher = null;
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Called whenever the debt is paid or created through a transaction,
|
|
205
|
-
* but not for interest accrual.
|
|
206
|
-
*
|
|
207
|
-
* @param {MethodContext} context
|
|
208
|
-
* @param {Amount} newDebt - principal and all accrued interest
|
|
209
|
-
*/
|
|
210
|
-
updateDebtSnapshot: ({ state }, newDebt) => {
|
|
211
|
-
const { manager } = state;
|
|
212
|
-
// update local state
|
|
213
|
-
state.debtSnapshot = newDebt;
|
|
214
|
-
state.interestSnapshot = manager.getCompoundedInterest();
|
|
215
|
-
},
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Update the debt balance and propagate upwards to
|
|
219
|
-
* maintain aggregate debt and liquidation order.
|
|
220
|
-
*
|
|
221
|
-
* @param {MethodContext} context
|
|
222
|
-
* @param {Amount} oldDebt - prior principal and all accrued interest
|
|
223
|
-
* @param {Amount} newDebt - actual principal and all accrued interest
|
|
224
|
-
*/
|
|
225
|
-
updateDebtAccounting: ({ state, facets }, oldDebt, newDebt) => {
|
|
226
|
-
const { manager } = state;
|
|
227
|
-
const { helper } = facets;
|
|
228
|
-
helper.updateDebtSnapshot(newDebt);
|
|
229
|
-
// update vault manager which tracks total debt
|
|
230
|
-
manager.applyDebtDelta(oldDebt, newDebt);
|
|
231
|
-
},
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* @param {MethodContext} context
|
|
235
|
-
*/
|
|
236
|
-
assertVaultHoldsNoMinted: ({ state, facets }) => {
|
|
237
|
-
const { vaultSeat } = state;
|
|
238
|
-
const { helper } = facets;
|
|
239
|
-
AmountMath.isEmpty(helper.getMintedAllocated(vaultSeat)) ||
|
|
240
|
-
assert.fail(X`Vault should be empty of debt`);
|
|
241
|
-
},
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Adjust principal and collateral (atomically for offer safety)
|
|
245
|
-
*
|
|
246
|
-
* @param {MethodContext} context
|
|
247
|
-
* @param {ZCFSeat} clientSeat
|
|
248
|
-
*/
|
|
249
|
-
adjustBalancesHook: ({ state, facets }, clientSeat) => {
|
|
250
|
-
const { collateralBrand, debtBrand, manager, vaultSeat } = state;
|
|
251
|
-
const { helper, pot } = facets;
|
|
252
|
-
assert(state.open);
|
|
253
|
-
|
|
254
|
-
const proposal = clientSeat.getProposal();
|
|
255
|
-
assertOnlyKeys(proposal, [KW.Attestation, KW.Debt]);
|
|
256
|
-
|
|
257
|
-
const debt = pot.getCurrentDebt();
|
|
258
|
-
const collateral = helper.getCollateralAllocated(vaultSeat);
|
|
259
|
-
|
|
260
|
-
const emptyCollateral = AmountMath.makeEmpty(
|
|
261
|
-
collateralBrand,
|
|
262
|
-
AssetKind.COPY_BAG,
|
|
263
|
-
);
|
|
264
|
-
const giveColl = proposal.give.Attestation || emptyCollateral;
|
|
265
|
-
const wantColl = proposal.want.Attestation || emptyCollateral;
|
|
266
|
-
|
|
267
|
-
// new = after the transaction gets applied
|
|
268
|
-
const newCollateral = addSubtract(collateral, giveColl, wantColl);
|
|
269
|
-
// max debt supported by current Collateral as modified by proposal
|
|
270
|
-
const { amountLiened, maxDebt: newMaxDebt } =
|
|
271
|
-
manager.maxDebtForLien(newCollateral);
|
|
272
|
-
|
|
273
|
-
const emptyDebt = AmountMath.makeEmpty(debtBrand);
|
|
274
|
-
const give = AmountMath.min(proposal.give.Debt || emptyDebt, debt);
|
|
275
|
-
const want = proposal.want.Debt || emptyDebt;
|
|
276
|
-
const giveDebtOnly = matches(
|
|
277
|
-
proposal,
|
|
278
|
-
harden({ give: { [KW.Debt]: M.record() }, want: {}, exit: M.any() }),
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
// Calculate the fee, the amount to mint and the resulting debt. We'll
|
|
282
|
-
// verify that the target debt doesn't violate the collateralization ratio,
|
|
283
|
-
// then mint, reallocate, and burn.
|
|
284
|
-
const { newDebt, fee, toMint } = calculateFee(
|
|
285
|
-
manager.getLoanFee(),
|
|
286
|
-
debt,
|
|
287
|
-
give,
|
|
288
|
-
want,
|
|
289
|
-
);
|
|
290
|
-
assert(
|
|
291
|
-
giveDebtOnly || AmountMath.isGTE(newMaxDebt, newDebt),
|
|
292
|
-
`cannot borrow ${q(newDebt)} against ${q(amountLiened)}; max is ${q(
|
|
293
|
-
newMaxDebt,
|
|
294
|
-
)}`,
|
|
295
|
-
);
|
|
296
|
-
|
|
297
|
-
trace('adjustBalancesHook', {
|
|
298
|
-
targetCollateralAmount: newCollateral,
|
|
299
|
-
vaultCollateral: newCollateral,
|
|
300
|
-
fee,
|
|
301
|
-
toMint,
|
|
302
|
-
newDebt,
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
stageDelta(clientSeat, vaultSeat, giveColl, wantColl, KW.Attestation);
|
|
306
|
-
stageDelta(clientSeat, vaultSeat, give, emptyDebt, KW.Debt);
|
|
307
|
-
manager.mintAndReallocate(toMint, fee, clientSeat, vaultSeat);
|
|
308
|
-
|
|
309
|
-
// parent needs to know about the change in debt
|
|
310
|
-
helper.updateDebtAccounting(debt, newDebt);
|
|
311
|
-
|
|
312
|
-
manager.burnDebt(give, vaultSeat);
|
|
313
|
-
|
|
314
|
-
helper.assertVaultHoldsNoMinted();
|
|
315
|
-
|
|
316
|
-
void helper.updateUiState();
|
|
317
|
-
clientSeat.exit();
|
|
318
|
-
|
|
319
|
-
return 'We have adjusted your balances; thank you for your business.';
|
|
320
|
-
},
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Given sufficient Minted payoff, refund the attestation.
|
|
324
|
-
*
|
|
325
|
-
* @type {import('@agoric/vat-data/src/types').PlusContext<MethodContext, OfferHandler>}
|
|
326
|
-
*/
|
|
327
|
-
closeHook: ({ state, facets }, seat) => {
|
|
328
|
-
const { debtBrand, manager, vaultSeat, zcf } = state;
|
|
329
|
-
const { helper, pot } = facets;
|
|
330
|
-
assert(state.open);
|
|
331
|
-
assertProposalShape(seat, {
|
|
332
|
-
give: { [KW.Debt]: null },
|
|
333
|
-
want: { [KW.Attestation]: null },
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
const currentDebt = pot.getCurrentDebt();
|
|
337
|
-
const {
|
|
338
|
-
give: { [KW.Debt]: runOffered },
|
|
339
|
-
} = seat.getProposal();
|
|
340
|
-
AmountMath.isGTE(runOffered, currentDebt) ||
|
|
341
|
-
assert.fail(
|
|
342
|
-
X`Offer ${runOffered} is not sufficient to pay off debt ${currentDebt}`,
|
|
343
|
-
);
|
|
344
|
-
vaultSeat.incrementBy(seat.decrementBy(harden({ [KW.Debt]: currentDebt })));
|
|
345
|
-
seat.incrementBy(
|
|
346
|
-
vaultSeat.decrementBy(
|
|
347
|
-
harden({ Attestation: vaultSeat.getAmountAllocated('Attestation') }),
|
|
348
|
-
),
|
|
349
|
-
);
|
|
350
|
-
|
|
351
|
-
zcf.reallocate(seat, vaultSeat);
|
|
352
|
-
|
|
353
|
-
manager.burnDebt(currentDebt, vaultSeat);
|
|
354
|
-
state.open = false;
|
|
355
|
-
helper.updateDebtSnapshot(AmountMath.makeEmpty(debtBrand));
|
|
356
|
-
void helper.updateUiState();
|
|
357
|
-
helper.assertVaultHoldsNoMinted();
|
|
358
|
-
seat.exit();
|
|
359
|
-
|
|
360
|
-
return 'Your stakeFactory is closed; thank you for your business.';
|
|
361
|
-
},
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
const potBehavior = {
|
|
365
|
-
/** @param {MethodContext} context */
|
|
366
|
-
getSubscriber: ({ state }) => state.subscriber,
|
|
367
|
-
/** @param {MethodContext} context */
|
|
368
|
-
makeAdjustBalancesInvitation: ({ state, facets }) => {
|
|
369
|
-
const { zcf } = state;
|
|
370
|
-
const { helper } = facets;
|
|
371
|
-
assert(state.open);
|
|
372
|
-
return zcf.makeInvitation(
|
|
373
|
-
seat => helper.adjustBalancesHook(seat),
|
|
374
|
-
'AdjustBalances',
|
|
375
|
-
);
|
|
376
|
-
},
|
|
377
|
-
/** @param {MethodContext} context */
|
|
378
|
-
makeCloseInvitation: ({ state, facets }) => {
|
|
379
|
-
const { zcf } = state;
|
|
380
|
-
const { helper } = facets;
|
|
381
|
-
assert(state.open);
|
|
382
|
-
return zcf.makeInvitation(seat => helper.closeHook(seat), 'CloseVault');
|
|
383
|
-
},
|
|
384
|
-
/**
|
|
385
|
-
* The actual current debt, including accrued interest.
|
|
386
|
-
*
|
|
387
|
-
* This looks like a simple getter but it does a lot of the heavy lifting for
|
|
388
|
-
* interest accrual. Rather than updating all records when interest accrues,
|
|
389
|
-
* the vault manager updates just its rolling compounded interest. Here we
|
|
390
|
-
* calculate what the current debt is given what's recorded in this vault and
|
|
391
|
-
* what interest has compounded since this vault record was written.
|
|
392
|
-
*
|
|
393
|
-
* @see getNormalizedDebt
|
|
394
|
-
* @param {MethodContext} context
|
|
395
|
-
* @returns {Amount<'nat'>}
|
|
396
|
-
*/
|
|
397
|
-
getCurrentDebt: ({ state }) => {
|
|
398
|
-
return calculateCurrentDebt(
|
|
399
|
-
state.debtSnapshot,
|
|
400
|
-
state.interestSnapshot,
|
|
401
|
-
state.manager.getCompoundedInterest(),
|
|
402
|
-
);
|
|
403
|
-
},
|
|
404
|
-
/** @param {MethodContext} context */
|
|
405
|
-
getNormalizedDebt: ({ state }) =>
|
|
406
|
-
reverseInterest(state.debtSnapshot, state.interestSnapshot),
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
const behavior = {
|
|
410
|
-
helper: helperBehavior,
|
|
411
|
-
pot: potBehavior,
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* @param {MethodContext} context
|
|
416
|
-
*/
|
|
417
|
-
const finish = ({ facets }) => {
|
|
418
|
-
const { helper } = facets;
|
|
419
|
-
void helper.updateUiState();
|
|
420
|
-
};
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* Make stakeFactory kit, subject to stakeFactory terms.
|
|
424
|
-
*
|
|
425
|
-
* @param {ZCF} zcf
|
|
426
|
-
* @param {ZCFSeat} startSeat
|
|
427
|
-
* @param {import('./stakeFactoryManager.js').StakeFactoryManager} manager
|
|
428
|
-
* @throws {Error} if startSeat proposal is not consistent with governance parameters in manager
|
|
429
|
-
*/
|
|
430
|
-
const makeStakeFactoryKitInternal = defineKindMulti(
|
|
431
|
-
'stakeFactoryKit',
|
|
432
|
-
initState,
|
|
433
|
-
behavior,
|
|
434
|
-
{ finish },
|
|
435
|
-
);
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Make stakeFactory kit, subject to stakeFactory terms.
|
|
439
|
-
*
|
|
440
|
-
* TODO refactor to remove this wrapper around the kind and to stop using
|
|
441
|
-
* `bindAllMethods`.
|
|
442
|
-
*
|
|
443
|
-
* @param {ZCF} zcf
|
|
444
|
-
* @param {ZCFSeat} startSeat
|
|
445
|
-
* @param {import('./stakeFactoryManager.js').StakeFactoryManager} manager
|
|
446
|
-
* @throws {Error} if startSeat proposal is not consistent with governance parameters in manager
|
|
447
|
-
*/
|
|
448
|
-
export const makeStakeFactoryKit = (zcf, startSeat, manager) => {
|
|
449
|
-
const { helper, pot } = makeStakeFactoryKitInternal(zcf, startSeat, manager);
|
|
450
|
-
return harden({
|
|
451
|
-
// XXX work- around for change from auto - binding
|
|
452
|
-
helper: bindAllMethods(helper),
|
|
453
|
-
pot,
|
|
454
|
-
});
|
|
455
|
-
};
|