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