@agoric/inter-protocol 0.13.2-dev-70ba9b0.0 → 0.13.2-dev-ed5ee58.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.
Files changed (70) hide show
  1. package/bundles/bundle-econCommitteeCharter-meta.js +78 -78
  2. package/bundles/bundle-econCommitteeCharter.js +1 -1
  3. package/bundles/bundle-feeDistributor-meta.js +61 -61
  4. package/bundles/bundle-feeDistributor.js +1 -1
  5. package/bundles/bundle-fluxAggregator-meta.js +188 -188
  6. package/bundles/bundle-fluxAggregator.js +1 -1
  7. package/bundles/bundle-psm-meta.js +112 -112
  8. package/bundles/bundle-psm.js +1 -1
  9. package/bundles/bundle-reserve-meta.js +110 -114
  10. package/bundles/bundle-reserve.js +1 -1
  11. package/bundles/bundle-scaledPriceAuthority-meta.js +78 -78
  12. package/bundles/bundle-stakeFactory-meta.js +118 -118
  13. package/bundles/bundle-stakeFactory.js +1 -1
  14. package/bundles/bundle-vaultFactory-meta.js +130 -130
  15. package/bundles/bundle-vaultFactory.js +1 -1
  16. package/exported.js +0 -1
  17. package/package.json +16 -16
  18. package/scripts/deploy-contracts.js +0 -2
  19. package/scripts/init-core.js +0 -16
  20. package/src/feeDistributor.js +1 -1
  21. package/src/proposals/addAssetToVault.js +4 -57
  22. package/src/proposals/committee-proposal.js +3 -6
  23. package/src/proposals/core-proposal.js +1 -73
  24. package/src/proposals/demoIssuers.js +1 -232
  25. package/src/proposals/econ-behaviors.js +10 -193
  26. package/src/proposals/sim-behaviors.js +1 -1
  27. package/src/reserve/assetReserve.js +9 -170
  28. package/src/reserve/params.js +1 -4
  29. package/src/vaultFactory/liquidateIncrementally.js +16 -141
  30. package/src/vaultFactory/liquidateMinimum.js +19 -21
  31. package/src/vaultFactory/params.js +0 -29
  32. package/src/vaultFactory/types.js +0 -6
  33. package/src/vaultFactory/vault.js +1 -3
  34. package/src/vaultFactory/vaultDirector.js +1 -33
  35. package/src/vaultFactory/vaultFactory.js +2 -12
  36. package/src/vaultFactory/vaultManager.js +10 -217
  37. package/bundles/bundle-amm-meta.js +0 -686
  38. package/bundles/bundle-amm.js +0 -1
  39. package/bundles/bundle-interchainPool-meta.js +0 -606
  40. package/bundles/bundle-interchainPool.js +0 -1
  41. package/bundles/bundle-liquidateIncrementally-meta.js +0 -470
  42. package/bundles/bundle-liquidateIncrementally.js +0 -1
  43. package/bundles/bundle-liquidateMinimum-meta.js +0 -470
  44. package/bundles/bundle-liquidateMinimum.js +0 -1
  45. package/scripts/add-initial-liquidity.js +0 -179
  46. package/src/interchainPool.js +0 -134
  47. package/src/vpool-xyk-amm/AttackersGuide.md +0 -89
  48. package/src/vpool-xyk-amm/addLiquidity.js +0 -221
  49. package/src/vpool-xyk-amm/addPool.js +0 -200
  50. package/src/vpool-xyk-amm/constantProduct/AMM-Trade.jpeg +0 -0
  51. package/src/vpool-xyk-amm/constantProduct/README.md +0 -204
  52. package/src/vpool-xyk-amm/constantProduct/calcFees.js +0 -93
  53. package/src/vpool-xyk-amm/constantProduct/calcSwapPrices.js +0 -83
  54. package/src/vpool-xyk-amm/constantProduct/core.js +0 -145
  55. package/src/vpool-xyk-amm/constantProduct/defaults.js +0 -3
  56. package/src/vpool-xyk-amm/constantProduct/getXY.js +0 -48
  57. package/src/vpool-xyk-amm/constantProduct/internal-types.js +0 -114
  58. package/src/vpool-xyk-amm/constantProduct/invariants.js +0 -39
  59. package/src/vpool-xyk-amm/constantProduct/swap.js +0 -270
  60. package/src/vpool-xyk-amm/constantProduct/types.js +0 -30
  61. package/src/vpool-xyk-amm/doublePool.js +0 -200
  62. package/src/vpool-xyk-amm/estimate.js +0 -175
  63. package/src/vpool-xyk-amm/multipoolMarketMaker.js +0 -530
  64. package/src/vpool-xyk-amm/params.js +0 -59
  65. package/src/vpool-xyk-amm/pool.js +0 -398
  66. package/src/vpool-xyk-amm/priceAuthority.js +0 -92
  67. package/src/vpool-xyk-amm/removeLiquidity.js +0 -29
  68. package/src/vpool-xyk-amm/singlePool.js +0 -88
  69. package/src/vpool-xyk-amm/swap.js +0 -68
  70. package/src/vpool-xyk-amm/types.js +0 -152
@@ -1,179 +0,0 @@
1
- import { AmountMath } from '@agoric/ertp';
2
- import { makeNotifierFromAsyncIterable } from '@agoric/notifier';
3
- import { E } from '@endo/far';
4
-
5
- const { entries } = Object;
6
-
7
- const dec6 = 10n ** 6n;
8
- const dec4 = 10n ** 4n;
9
-
10
- const config = {
11
- Central: { IST: 10_000n * dec6 },
12
- Secondary: { IbcATOM: 500n * dec4 }, // 20 IST per ATOM
13
- };
14
-
15
- const getOfferResult = async (instance, wallet, walletAdmin) => {
16
- const offerIt = E(walletAdmin).getOffersNotifier();
17
- let result;
18
- console.log('awaiting offers...');
19
- for await (const offers of makeNotifierFromAsyncIterable(offerIt)) {
20
- console.log('offers iterator next:', offers);
21
- offers
22
- .filter(
23
- ({ status, invitationQuery }) =>
24
- status === 'complete' && invitationQuery.instance === instance,
25
- )
26
- .forEach(async ({ id }) => {
27
- result = await E(wallet).lookup('offerResult', id);
28
- console.log({ facet: result });
29
- });
30
- if (result) break;
31
- }
32
- return result;
33
- };
34
-
35
- /**
36
- *
37
- * @param { { agoricNames: ERef<NameHub>, scratch: ERef<import('@agoric/solo/src/scratch').ScratchPad>, wallet: ERef<WalletAdmin>, zoe: ERef<ZoeService>} } homeP
38
- * @param { {now?: () => number }} _endowments
39
- *
40
- * @typedef {ReturnType<typeof import('../../wallet/api/src/lib-wallet').makeWalletRoot>['admin']} Wallet
41
- */
42
- const addInitialLiquidity = async (homeP, { now = () => Date.now() }) => {
43
- const { wallet, agoricNames, zoe, scratch } = E.get(homeP);
44
-
45
- const walletBridge = E(wallet).getBridge();
46
-
47
- const getService = async name => {
48
- console.log('lookup instance', name);
49
- const instance = await E(agoricNames).lookup('instance', name);
50
- const pub = E(zoe).getPublicFacet(instance);
51
- return { instance, pub };
52
- };
53
- const amm = await getService('amm');
54
-
55
- const addUsingWallet = async () => {
56
- console.log('getting purses from walletBridge...');
57
- const { value: pStates } = await E(
58
- E(walletBridge).getPursesNotifier(),
59
- ).getUpdateSince();
60
- console.log(pStates);
61
-
62
- const toAmt = detail => {
63
- const [[name, value]] = entries(detail);
64
- const { pursePetname } =
65
- pStates.find(p => p.brandPetname === name) ||
66
- assert.fail(`cannot find purse ${name}`);
67
- return { pursePetname, value };
68
- };
69
- const proposalTemplate = {
70
- give: {
71
- Central: toAmt(config.Central),
72
- Secondary: toAmt(config.Secondary),
73
- },
74
- };
75
- console.log({ proposalTemplate });
76
-
77
- console.log('amm.makeAddLiquidityInvitation...');
78
- const invitation = await E(amm.pub).makeAddLiquidityInvitation();
79
- console.log({ invitation });
80
-
81
- const offerConfig = {
82
- id: `${now()}`,
83
- invitation,
84
- instanceHandle: amm.instance,
85
- proposalTemplate,
86
- requestContext: undefined,
87
- };
88
- console.log('addOffer', offerConfig);
89
- const added = await E(walletBridge).addOffer(offerConfig);
90
- console.log({ added });
91
-
92
- const result = await getOfferResult(
93
- amm.instance,
94
- wallet,
95
- E(wallet).getAdminFacet(),
96
- );
97
- console.log({ result });
98
- };
99
-
100
- const addUsingZoe = async () => {
101
- const { keys, values } = Object;
102
- /** @type {<K extends string, V>(entries: [K, V][]) => Record<K, V>} */
103
- const toRecord = Object.fromEntries;
104
-
105
- console.log('getting purses, issuers from wallet...');
106
- const [purseEntries, { value: issuerEntries }] = await Promise.all([
107
- E(wallet).getPurses(),
108
- E(E(walletBridge).getIssuersNotifier()).getUpdateSince(),
109
- ]);
110
-
111
- // @ts-expect-error Petname can be string[]
112
- const purses = toRecord(purseEntries);
113
- // @ts-expect-error Petname can be string[]
114
- const issuers = toRecord(issuerEntries);
115
- console.log({ purses: keys(purses), issuers: keys(issuers) });
116
- assert(
117
- issuers.IbcATOM,
118
- 'Issuers not complete. Try again after purses are provisioned',
119
- );
120
- const liqIssuer = E(amm.pub).getLiquidityIssuer(issuers.IbcATOM.brand);
121
- const liqBrand = await E(liqIssuer).getBrand();
122
-
123
- /** @param {Record<string, bigint>} detail */
124
- const toAmt = detail => {
125
- const [[name, value]] = entries(detail);
126
- const issuer = issuers[name];
127
- assert(issuer);
128
- return AmountMath.make(issuer.brand, value);
129
- };
130
- /** @param {Amount} amt */
131
- const withdraw = async amt => {
132
- for await (const purse of values(purses)) {
133
- const b2 = await E(purse).getAllegedBrand();
134
- if (b2 === amt.brand) {
135
- return E(purse).withdraw(amt);
136
- }
137
- }
138
- throw Error(`no purse found for ${amt.brand}`);
139
- };
140
- const proposal = {
141
- want: {
142
- Liquidity: AmountMath.makeEmpty(liqBrand),
143
- },
144
- give: {
145
- Central: toAmt(config.Central),
146
- Secondary: toAmt(config.Secondary),
147
- },
148
- };
149
- console.log({ proposal });
150
- console.log('withdraw payments...', proposal.give);
151
- const payments = {
152
- Central: await withdraw(proposal.give.Central),
153
- Secondary: await withdraw(proposal.give.Secondary),
154
- };
155
- console.log('payments', payments);
156
- console.log('amm.makeAddLiquidityInvitation...');
157
- const invitation = await E(amm.pub).makeAddLiquidityInvitation();
158
- console.log({ invitation });
159
-
160
- const seat = await E(zoe).offer(invitation, proposal, payments);
161
- console.log({ seat });
162
-
163
- const result = await E(seat).getOfferResult();
164
- console.log({ result });
165
-
166
- const payouts = await E(seat).getPayouts();
167
- console.log('initialLiquidityPayouts', keys(payouts));
168
- await E(scratch).set('initialLiquidityPayouts', payouts);
169
- };
170
-
171
- console.log(
172
- addUsingWallet,
173
- 'does not handle want Liquidity so we do not use it',
174
- );
175
- await addUsingZoe();
176
- };
177
- harden(addInitialLiquidity);
178
-
179
- export default addInitialLiquidity;
@@ -1,134 +0,0 @@
1
- import { E, Far } from '@endo/far';
2
- import { AmountMath, AssetKind, makeIssuerKit } from '@agoric/ertp';
3
- import {
4
- offerTo,
5
- atomicTransfer,
6
- } from '@agoric/zoe/src/contractSupport/index.js';
7
- import { MIN_INITIAL_POOL_LIQUIDITY_KEY } from './vpool-xyk-amm/params.js';
8
-
9
- const { Fail, quote: q } = assert;
10
-
11
- const COSMOS_DECIMALS = 6;
12
-
13
- /**
14
- * Given sufficient IST, create an issuer for an IBC denom
15
- * and an invitation to add a pool for it to the AMM.
16
- *
17
- * @param {ZCF<InterchainPoolTerms>} zcf
18
- * @param {{
19
- * bankManager: ERef<BankManager>,
20
- * }} privateArgs
21
- *
22
- * @typedef {{
23
- * amm: Instance,
24
- * }} InterchainPoolTerms
25
- *
26
- * @typedef {{
27
- * denom: string,
28
- * decimalPlaces: number,
29
- * }} AssetDetail
30
- */
31
- export const start = (zcf, { bankManager }) => {
32
- const { amm } = zcf.getTerms();
33
-
34
- const zoe = zcf.getZoeService();
35
- /** @type {ERef<GovernedPublicFacet<XYKAMMPublicFacet>>} */
36
- const ammPub = E(zoe).getPublicFacet(amm);
37
-
38
- let kwNonce = 0;
39
-
40
- /**
41
- * @param {ZCFSeat} seat
42
- * @param {AssetDetail} detail
43
- */
44
- const step1Handler = async (seat, detail) => {
45
- assert.typeof(
46
- detail,
47
- 'object',
48
- 'offer arg must be { denom, decimalPlaces? }',
49
- );
50
- const { denom, decimalPlaces = COSMOS_DECIMALS } = detail;
51
- assert.typeof(denom, 'string');
52
- assert.typeof(decimalPlaces, 'number');
53
-
54
- const minimumCentral = await E(ammPub).getAmount(
55
- MIN_INITIAL_POOL_LIQUIDITY_KEY,
56
- );
57
-
58
- const {
59
- give: { Central: centralAmt },
60
- } = seat.getProposal();
61
- AmountMath.isGTE(centralAmt, minimumCentral) ||
62
- Fail`at least ${q(minimumCentral)} required; only ${q(centralAmt)} given`;
63
-
64
- const interAsset = makeIssuerKit(
65
- denom,
66
- AssetKind.NAT,
67
- harden({ decimalPlaces }),
68
- );
69
- kwNonce += 1;
70
-
71
- await zcf.saveIssuer(interAsset.issuer, `Interchain${kwNonce}`);
72
-
73
- /** @type { OfferHandler } */
74
- const step2Handler = async seat2 => {
75
- const {
76
- give: { Secondary: secondaryAmt },
77
- } = seat2.getProposal();
78
- AmountMath.coerce(interAsset.brand, secondaryAmt);
79
-
80
- const liquidityIssuer = await E(ammPub).addIssuer(
81
- interAsset.issuer,
82
- `Interchain${kwNonce}`,
83
- );
84
- await zcf.saveIssuer(liquidityIssuer, `Liquidity${kwNonce}`);
85
-
86
- const proposal = harden({
87
- give: {
88
- Central: centralAmt,
89
- Secondary: secondaryAmt,
90
- },
91
- });
92
-
93
- atomicTransfer(zcf, seat, seat2, { Central: centralAmt });
94
-
95
- const invitation = await E(ammPub).addPoolInvitation();
96
- const { userSeatPromise, deposited } = await offerTo(
97
- zcf,
98
- invitation,
99
- undefined,
100
- proposal,
101
- seat2,
102
- );
103
- return E.when(deposited, _ => {
104
- seat.exit();
105
- seat2.exit();
106
-
107
- return E(userSeatPromise).getOfferResult();
108
- });
109
- };
110
-
111
- const keyword = denom; // ISSUE #5412: should not show up in all wallets.
112
- const proposedName = denom;
113
- const [invitation] = await Promise.all([
114
- zcf.makeInvitation(step2Handler, 'interchain pool step 2'),
115
- E(bankManager).addAsset(
116
- denom,
117
- keyword,
118
- proposedName,
119
- interAsset, // with mint
120
- ),
121
- ]);
122
-
123
- return harden({
124
- issuer: interAsset.issuer,
125
- invitation,
126
- });
127
- };
128
-
129
- const publicFacet = Far('InterchainPoolGateway', {
130
- makeInterchainPoolInvitation: () =>
131
- zcf.makeInvitation(step1Handler, 'interchain pool step 1'),
132
- });
133
- return { publicFacet };
134
- };
@@ -1,89 +0,0 @@
1
- # An Attacker's guide to the Constant Product AMM
2
-
3
- This is an incomplete list of potential weak points that an attacker might want to focus
4
- on when looking for ways to violate the integrity of the AMM. It's here to help defenders,
5
- as "attacker's mindset" is a good way to focus attention for the defender. The list should
6
- correspond pretty closely to the set of assurances that the AMM aims to support.
7
-
8
- The main guarantee that an
9
- [Automated Market Maker](https://mason.gmu.edu/~rhanson/mktscore.pdf) makes is that
10
- sufficiently liquid pools will always retain enough collateral through any sequence of
11
- trades to be able to continue trading. The caveat just means that when the amount of
12
- liquidity in the pool is quite small with respect to desired trades, trades will quickly
13
- push the balance to extreme enough prices that no further trading will take place. If the
14
- pool is liquid enough, trades will push the price enough out of equilibrium that it will
15
- be very attractive for arbitrageurs to restore the price. Anything that allows traders or
16
- liquidity providers to violate that expectation is a problem.
17
-
18
- ## Multiple Pools
19
-
20
- `addPool` and `addLiquidity` are publicly available. That means someone could maliciously
21
- create pools or provide insufficient liquidity. The requirement of a minimum initial
22
- liquidity should be enough to prevent misuse of these APIs from hurting us.
23
-
24
- Since `addPool` is public, someone could add a look-alike issuer for a well-known
25
- currency and make it harder for people to find the pool they intend to trade with. As
26
- long as people are dealing wiht Brands and Issuers, they shouldn't be confused by
27
- this. Does the Wallet UI provide enough clarity in the face of this?
28
-
29
- Zoe holds the collaterral for all the pools in common. We segragate it by Pool, so each
30
- pool's allocation includes some IST (the central currency) and some of another
31
- collateral. The doublePool trades move IST between pools, but Zoe's rights conservation
32
- enforcement ensure that none is ever gained or lost.
33
-
34
- ## Fees
35
-
36
- Fees are collected as trading takes place, and kept in a distinct Zoe seat. The creator of
37
- the AMM should be the only one who can collect the fees. Can that invitation be hijacked
38
- from whoever gets that facet during bootstrap? Is there another way to steal fees?
39
-
40
- Can users structure trades so they can pay less in fees than intended?
41
-
42
- ## Providing Liquidity
43
-
44
- Traders can provide liquidity and get liquidity tokens for a particular pool. The goal is
45
- that they earn a share of the fees that were collected while their liquidity remained in
46
- the pool. Can liquidity providers gain extra fees over other providers, or can they cause
47
- traders to be disadvantaged?
48
-
49
- Can liquidity providers extact more from the pool than they should be able to?
50
-
51
- ## Math
52
-
53
- The whole design is predicated on the X*Y=K invariant. That invariant says that trading
54
- should not change the product of X and Y, but in actuality, roundoff and fees are both
55
- allowed to change the product. Roundoff should increase it ever so slightly, except in
56
- cases where there are small amounts in the pool, where it can be significant. Fees are
57
- added to the pool, so they change the size of the pool, but should be interpreted as
58
- taking place after the trade rebalances the amounts. The equations are explained in detail
59
- in `constantProduct/README.md`.
60
-
61
- Adding liquidity adds funds to both pools so, of course, it changes X and Y. But (except
62
- in the case of `AddLiquidityAtRate`), it should keep X and Y in the same proportion.
63
-
64
- I don't know whether anyone else has done an implementation of the multi-pool swap that
65
- works like ours. If neither X nor Y is IST, then we trade X for IST and IST for
66
- Y. Attributing fees in this case took some art. This is also addressed in
67
- `constantProduct/README.md`.
68
-
69
- Adding liquidity while changing the ratio of assets is done in two steps in order to
70
- prevent people from using a single step as a way to avoid fees on trades. Users shouldn't
71
- be able to use `AddLiquidityAtRate` to avoid paying fees on what is effectively a trade.
72
-
73
-
74
- ## Economics
75
-
76
- The Inter Protocol mechanism is envisioned as a major provider of liquidity. It may leave
77
- its liquidity in the pool over the long term, and never or seldom collect earnings. Does
78
- this affect prices or trading volumes adverselyj? (More liquidity is expected to provide
79
- better prices for traders, so this would be a surprise.)
80
-
81
- Other constant product markets have performed reasonably well through periods of high
82
- volatillity. Is there any reason to expect the design variations we've introduced to have
83
- trouble where others did not?
84
-
85
- ## Governance
86
-
87
- The poolFee and protocolFee can be changed by governance. Can unauthorized parties
88
- interfere with this? Is there any hazard here beyond the possibility that the holders of
89
- this power might raise rates more than some users would be happy with?
@@ -1,221 +0,0 @@
1
- import {
2
- assertProposalShape,
3
- calcSecondaryRequired,
4
- natSafeMath,
5
- } from '@agoric/zoe/src/contractSupport/index.js';
6
- import { AmountMath } from '@agoric/ertp';
7
-
8
- import '@agoric/zoe/exported.js';
9
-
10
- const { Fail, quote: q } = assert;
11
-
12
- const { add, multiply } = natSafeMath;
13
- /**
14
- * @param {ZCF} zcf
15
- * @param {(brand: Brand) => XYKPool} getPool
16
- */
17
- const makeMakeAddLiquidityInvitation = (zcf, getPool) => {
18
- const addLiquidity = seat => {
19
- assertProposalShape(seat, {
20
- give: {
21
- Central: null,
22
- Secondary: null,
23
- },
24
- });
25
-
26
- // Get the brand of the secondary token so we can identify the liquidity pool.
27
- const secondaryBrand = seat.getProposal().give.Secondary.brand;
28
- const pool = getPool(secondaryBrand);
29
- const liquidityIssuer = pool.getLiquidityIssuer();
30
- const liquidityBrand = zcf.getBrandForIssuer(liquidityIssuer);
31
- seat.getProposal().want.Liquidity.brand === liquidityBrand ||
32
- Fail`liquidity brand must be ${q(liquidityBrand)}`;
33
-
34
- return pool.addLiquidity(seat);
35
- };
36
-
37
- const makeAddLiquidityInvitation = () =>
38
- zcf.makeInvitation(addLiquidity, 'multipool amm add liquidity');
39
-
40
- return makeAddLiquidityInvitation;
41
- };
42
-
43
- // The desired ratio requires a wider range than K would support.
44
- const imbalancedRequest = (desiredRatio, startK) => {
45
- if (desiredRatio > 1.0) {
46
- return desiredRatio > Number(startK);
47
- } else {
48
- return Number(startK) * desiredRatio < 1n;
49
- }
50
- };
51
- harden(imbalancedRequest);
52
- // exported for testing only
53
- export { imbalancedRequest };
54
-
55
- /**
56
- * The pool has poolX and poolY currently. The user wants to add liquidity of
57
- * giveX and giveY, but the ratios are probably not the same. We want to adjust
58
- * the pool to have the ratio (poolX + giveX) / (poolY + giveY), without
59
- * changing K, the product of the two sides.
60
- *
61
- * Calculate targetX and targetY, which multiply to the same product as
62
- * poolX * poolY, but are in the ratio we'll end at. From this we can produce
63
- * a proposed trade that maintains K, but changes the ratio, so we can add
64
- * liquidity at the desired ratio.
65
- *
66
- * endX = poolX + giveX; endY = poolY + giveY
67
- * desiredRatio = endX / endY
68
- * targetY = sqrt(startK / desiredRatio)
69
- * targetX = desiredRatio * targetY
70
- * so targetK equals startK because we square targetY
71
- * targetK = targetX * targetY = desiredRatio * (startK / desiredRatio)
72
- *
73
- * Since startK/endK is less than one, and we have to worry about early loss of
74
- * precision, we round and convert to bigint as the last step
75
- *
76
- * If the desired ratio (the ratio of the sums of contributed amounts and pool
77
- * balances) is larger than K (which most often happens when the pool balances
78
- * are small) we somewhat arbitrarily refuse the transaction.
79
- * For instance, if the desired ratio is 1000:1 because one currency uses 12
80
- * digits and the other 9, and the pool has single digits of liquidity, then
81
- * it would be better to manually adjust rather than using this approach.
82
- *
83
- * @param {Amount<'nat'>} poolX
84
- * @param {Amount<'nat'>} poolY
85
- * @param {Amount<'nat'>} giveX
86
- * @param {Amount<'nat'>} giveY
87
- * @returns {{targetX: Amount<'nat'>, targetY: Amount<'nat'> }}
88
- */
89
- export const balancesToReachRatio = (poolX, poolY, giveX, giveY) => {
90
- const startK = multiply(poolX.value, poolY.value);
91
- const endX = add(poolX.value, giveX.value);
92
- const endY = add(poolY.value, giveY.value);
93
- const desiredRatio = Number(endX) / Number(endY);
94
-
95
- if (imbalancedRequest(desiredRatio, startK)) {
96
- return {
97
- targetX: AmountMath.makeEmpty(poolX.brand),
98
- targetY: AmountMath.makeEmpty(poolY.brand),
99
- };
100
- }
101
- const targetY = Math.sqrt(Number(startK) / desiredRatio);
102
- const targetX = targetY * desiredRatio;
103
-
104
- return {
105
- targetX: AmountMath.make(poolX.brand, BigInt(Math.trunc(targetX))),
106
- targetY: AmountMath.make(poolY.brand, BigInt(Math.trunc(targetY))),
107
- };
108
- };
109
-
110
- /**
111
- * @param {ZCF} zcf
112
- * @param {(b: Brand) => XYKPool} getPool
113
- * @param {(i: Brand) => VirtualPool} provideVPool
114
- * @param {ZCFSeat} feeSeat
115
- * @param {(b: Brand) => PoolFacets['helper']} getPoolHelper
116
- */
117
- const makeMakeAddLiquidityAtRateInvitation = (
118
- zcf,
119
- getPool,
120
- provideVPool,
121
- feeSeat,
122
- getPoolHelper,
123
- ) => {
124
- const addLiquidityAtRate = seat => {
125
- assertProposalShape(seat, {
126
- give: {
127
- Central: null,
128
- Secondary: null,
129
- },
130
- want: { Liquidity: null },
131
- });
132
-
133
- const giveAlloc = seat.getProposal().give;
134
- /** @type {Amount<'nat'>} */
135
- const secondaryAmount = giveAlloc.Secondary;
136
- const secondaryBrand = secondaryAmount.brand;
137
- const centralBrand = giveAlloc.Central.brand;
138
- const pool = getPool(secondaryBrand);
139
- // Step 1: trade to adjust the pool's price
140
- // A figure out the ratio of the inputs
141
- // B figure out how X*Y changes to reach that ratio (ignoring fees)
142
- const centralPoolAmount = pool.getCentralAmount();
143
- const secondaryPoolAmount = pool.getSecondaryAmount();
144
-
145
- if (
146
- AmountMath.isEmpty(centralPoolAmount) &&
147
- AmountMath.isEmpty(secondaryPoolAmount)
148
- ) {
149
- return pool.addLiquidity(seat);
150
- }
151
-
152
- const { targetX: newCentral, targetY: newSecondary } = balancesToReachRatio(
153
- centralPoolAmount,
154
- secondaryPoolAmount,
155
- giveAlloc.Central,
156
- giveAlloc.Secondary,
157
- );
158
-
159
- const vPool = provideVPool(secondaryBrand);
160
- const poolSeat = pool.getPoolSeat();
161
- const transferForTrade = (prices, incrementKey, decrementKey) => {
162
- seat.decrementBy(harden({ [incrementKey]: prices.swapperGives }));
163
- poolSeat.decrementBy(harden({ [decrementKey]: prices.yDecrement }));
164
- seat.incrementBy(harden({ [decrementKey]: prices.swapperGets }));
165
- feeSeat.incrementBy(harden({ Fee: prices.protocolFee }));
166
- poolSeat.incrementBy(harden({ [incrementKey]: prices.xIncrement }));
167
- };
168
-
169
- // 1C Stage the changes for the trade
170
- if (AmountMath.isGTE(newCentral, centralPoolAmount)) {
171
- const prices = vPool.getPriceForOutput(
172
- AmountMath.makeEmpty(centralBrand),
173
- AmountMath.subtract(secondaryPoolAmount, newSecondary),
174
- );
175
- transferForTrade(prices, 'Central', 'Secondary');
176
- } else {
177
- const prices = vPool.getPriceForInput(
178
- AmountMath.subtract(newSecondary, secondaryPoolAmount),
179
- AmountMath.makeEmpty(centralBrand),
180
- );
181
- transferForTrade(prices, 'Secondary', 'Central');
182
- }
183
-
184
- // Step 2: add remaining liquidity
185
- const stagedAllocation = poolSeat.getStagedAllocation();
186
- const centralPoolAfterTrade = stagedAllocation.Central;
187
- const secondaryPoolAfterTrade = stagedAllocation.Secondary;
188
- const userAllocation = seat.getStagedAllocation();
189
-
190
- const secondaryRequired = AmountMath.make(
191
- secondaryBrand,
192
- calcSecondaryRequired(
193
- userAllocation.Central.value,
194
- centralPoolAfterTrade.value,
195
- secondaryPoolAfterTrade.value,
196
- userAllocation.Secondary.value,
197
- ),
198
- );
199
-
200
- return getPoolHelper(secondaryBrand).addLiquidityActual(
201
- pool,
202
- seat,
203
- secondaryRequired,
204
- poolSeat.getStagedAllocation().Central,
205
- feeSeat,
206
- );
207
- };
208
-
209
- const makeAddLiquidityInvitation = () =>
210
- zcf.makeInvitation(
211
- addLiquidityAtRate,
212
- 'multipool amm add liquidity at rate',
213
- );
214
-
215
- return makeAddLiquidityInvitation;
216
- };
217
-
218
- harden(makeMakeAddLiquidityInvitation);
219
- harden(makeMakeAddLiquidityAtRateInvitation);
220
-
221
- export { makeMakeAddLiquidityInvitation, makeMakeAddLiquidityAtRateInvitation };