@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.
- package/bundles/bundle-econCommitteeCharter-meta.js +78 -78
- package/bundles/bundle-econCommitteeCharter.js +1 -1
- package/bundles/bundle-feeDistributor-meta.js +61 -61
- package/bundles/bundle-feeDistributor.js +1 -1
- package/bundles/bundle-fluxAggregator-meta.js +188 -188
- package/bundles/bundle-fluxAggregator.js +1 -1
- package/bundles/bundle-psm-meta.js +112 -112
- package/bundles/bundle-psm.js +1 -1
- package/bundles/bundle-reserve-meta.js +110 -114
- package/bundles/bundle-reserve.js +1 -1
- package/bundles/bundle-scaledPriceAuthority-meta.js +78 -78
- package/bundles/bundle-stakeFactory-meta.js +118 -118
- package/bundles/bundle-stakeFactory.js +1 -1
- package/bundles/bundle-vaultFactory-meta.js +130 -130
- package/bundles/bundle-vaultFactory.js +1 -1
- package/exported.js +0 -1
- package/package.json +16 -16
- package/scripts/deploy-contracts.js +0 -2
- package/scripts/init-core.js +0 -16
- package/src/feeDistributor.js +1 -1
- package/src/proposals/addAssetToVault.js +4 -57
- package/src/proposals/committee-proposal.js +3 -6
- package/src/proposals/core-proposal.js +1 -73
- package/src/proposals/demoIssuers.js +1 -232
- package/src/proposals/econ-behaviors.js +10 -193
- package/src/proposals/sim-behaviors.js +1 -1
- package/src/reserve/assetReserve.js +9 -170
- package/src/reserve/params.js +1 -4
- package/src/vaultFactory/liquidateIncrementally.js +16 -141
- package/src/vaultFactory/liquidateMinimum.js +19 -21
- package/src/vaultFactory/params.js +0 -29
- package/src/vaultFactory/types.js +0 -6
- package/src/vaultFactory/vault.js +1 -3
- package/src/vaultFactory/vaultDirector.js +1 -33
- package/src/vaultFactory/vaultFactory.js +2 -12
- package/src/vaultFactory/vaultManager.js +10 -217
- package/bundles/bundle-amm-meta.js +0 -686
- package/bundles/bundle-amm.js +0 -1
- package/bundles/bundle-interchainPool-meta.js +0 -606
- package/bundles/bundle-interchainPool.js +0 -1
- package/bundles/bundle-liquidateIncrementally-meta.js +0 -470
- package/bundles/bundle-liquidateIncrementally.js +0 -1
- package/bundles/bundle-liquidateMinimum-meta.js +0 -470
- package/bundles/bundle-liquidateMinimum.js +0 -1
- package/scripts/add-initial-liquidity.js +0 -179
- package/src/interchainPool.js +0 -134
- package/src/vpool-xyk-amm/AttackersGuide.md +0 -89
- package/src/vpool-xyk-amm/addLiquidity.js +0 -221
- package/src/vpool-xyk-amm/addPool.js +0 -200
- 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 -93
- package/src/vpool-xyk-amm/constantProduct/calcSwapPrices.js +0 -83
- package/src/vpool-xyk-amm/constantProduct/core.js +0 -145
- package/src/vpool-xyk-amm/constantProduct/defaults.js +0 -3
- package/src/vpool-xyk-amm/constantProduct/getXY.js +0 -48
- package/src/vpool-xyk-amm/constantProduct/internal-types.js +0 -114
- package/src/vpool-xyk-amm/constantProduct/invariants.js +0 -39
- package/src/vpool-xyk-amm/constantProduct/swap.js +0 -270
- package/src/vpool-xyk-amm/constantProduct/types.js +0 -30
- package/src/vpool-xyk-amm/doublePool.js +0 -200
- package/src/vpool-xyk-amm/estimate.js +0 -175
- package/src/vpool-xyk-amm/multipoolMarketMaker.js +0 -530
- package/src/vpool-xyk-amm/params.js +0 -59
- package/src/vpool-xyk-amm/pool.js +0 -398
- package/src/vpool-xyk-amm/priceAuthority.js +0 -92
- package/src/vpool-xyk-amm/removeLiquidity.js +0 -29
- package/src/vpool-xyk-amm/singlePool.js +0 -88
- package/src/vpool-xyk-amm/swap.js +0 -68
- package/src/vpool-xyk-amm/types.js +0 -152
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import { E } from '@endo/eventual-send';
|
|
2
|
-
import { AmountMath, AssetKind } from '@agoric/ertp';
|
|
3
|
-
import {
|
|
4
|
-
assertProposalShape,
|
|
5
|
-
atomicTransfer,
|
|
6
|
-
} from '@agoric/zoe/src/contractSupport/index.js';
|
|
7
|
-
|
|
8
|
-
import { definePoolKind } from './pool.js';
|
|
9
|
-
|
|
10
|
-
const { quote: q, Fail } = assert;
|
|
11
|
-
|
|
12
|
-
const DISPLAY_INFO = harden({ decimalPlaces: 6 });
|
|
13
|
-
|
|
14
|
-
/** @typedef {import('@agoric/vat-data').Baggage} Baggage */
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @param {ZCF} zcf
|
|
18
|
-
* @param {(Brand) => boolean} isInSecondaries
|
|
19
|
-
* @param {import('@agoric/store/src/stores/store-utils.js').AtomicProvider<Brand<'nat'>, ZCFMint<'nat'>>} brandToLiquidityMintProvider
|
|
20
|
-
* @param {() => (secondaryBrand: Brand<'nat'>) => Promise<void>} getAddIssuerToReserve
|
|
21
|
-
*/
|
|
22
|
-
export const makeAddIssuer = (
|
|
23
|
-
zcf,
|
|
24
|
-
isInSecondaries,
|
|
25
|
-
brandToLiquidityMintProvider,
|
|
26
|
-
getAddIssuerToReserve,
|
|
27
|
-
) => {
|
|
28
|
-
/**
|
|
29
|
-
* Add a new issuer. If we previously received a request for the same issuer,
|
|
30
|
-
* we'll return the issuer or a promise for it.
|
|
31
|
-
*
|
|
32
|
-
* @param {import('./pool.js').MethodContext} _context
|
|
33
|
-
* @param {Issuer<'nat'>} secondaryIssuer
|
|
34
|
-
* @param {string} keyword
|
|
35
|
-
*/
|
|
36
|
-
const addIssuer = async (_context, secondaryIssuer, keyword) => {
|
|
37
|
-
const [secondaryAssetKind, secondaryBrand] = await Promise.all([
|
|
38
|
-
E(secondaryIssuer).getAssetKind(),
|
|
39
|
-
E(secondaryIssuer).getBrand(),
|
|
40
|
-
]);
|
|
41
|
-
// AWAIT ///////////////
|
|
42
|
-
secondaryAssetKind === AssetKind.NAT ||
|
|
43
|
-
Fail`${q(keyword)} asset not fungible (must use NAT math)`;
|
|
44
|
-
|
|
45
|
-
/** @type {(brand: Brand<'nat'>) => Promise<ZCFMint<'nat'>>} */
|
|
46
|
-
const makeLiquidityMint = brand => {
|
|
47
|
-
!isInSecondaries(brand) ||
|
|
48
|
-
Fail`issuer ${secondaryIssuer} already has a pool`;
|
|
49
|
-
|
|
50
|
-
const liquidityKeyword = `${keyword}Liquidity`;
|
|
51
|
-
zcf.assertUniqueKeyword(liquidityKeyword);
|
|
52
|
-
|
|
53
|
-
return E.when(
|
|
54
|
-
Promise.all([
|
|
55
|
-
zcf.saveIssuer(secondaryIssuer, keyword),
|
|
56
|
-
zcf.makeZCFMint(liquidityKeyword, AssetKind.NAT, DISPLAY_INFO),
|
|
57
|
-
]),
|
|
58
|
-
([issuer, mint]) => {
|
|
59
|
-
console.log(
|
|
60
|
-
`Saved issuer ${secondaryIssuer} to keyword ${keyword} and got back ${issuer}`,
|
|
61
|
-
);
|
|
62
|
-
return mint;
|
|
63
|
-
},
|
|
64
|
-
).catch(e => {
|
|
65
|
-
console.error(
|
|
66
|
-
`Failure Saving issuer ${secondaryIssuer}. Not added to Reserve`,
|
|
67
|
-
);
|
|
68
|
-
throw e;
|
|
69
|
-
});
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
/** @type {(brand: Brand<'nat'>) => Promise<void>} */
|
|
73
|
-
const finish = brand => {
|
|
74
|
-
// defer lookup until necessary. more aligned with governed
|
|
75
|
-
// param we expect this to be eventually.
|
|
76
|
-
const addIssuerToReserve = getAddIssuerToReserve();
|
|
77
|
-
|
|
78
|
-
// tell the reserve about this brand, which it will validate by
|
|
79
|
-
// calling back to AMM for the issuer
|
|
80
|
-
return addIssuerToReserve(brand);
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
return brandToLiquidityMintProvider
|
|
84
|
-
.provideAsync(secondaryBrand, makeLiquidityMint, finish)
|
|
85
|
-
.then(mint => mint.getIssuerRecord().issuer);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
return addIssuer;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* @param {Baggage} baggage
|
|
93
|
-
* @param {import('./multipoolMarketMaker.js').AmmPowers} ammPowers
|
|
94
|
-
* @param {(secondaryBrand: Brand, reserveLiquidityTokenSeat: ZCFSeat, liquidityKeyword: Keyword) => Promise<void>} onOfferHandled
|
|
95
|
-
* @param {ERef<StorageNode>} storageNode
|
|
96
|
-
* @param {ERef<Marshaller>} marshaller
|
|
97
|
-
*/
|
|
98
|
-
export const makeAddPoolInvitation = (
|
|
99
|
-
baggage,
|
|
100
|
-
ammPowers,
|
|
101
|
-
onOfferHandled,
|
|
102
|
-
storageNode,
|
|
103
|
-
marshaller,
|
|
104
|
-
) => {
|
|
105
|
-
const { zcf } = ammPowers;
|
|
106
|
-
const makePool = definePoolKind(baggage, ammPowers, storageNode, marshaller);
|
|
107
|
-
|
|
108
|
-
/** @type {(Brand) => Promise<{poolFacets: PoolFacets, liquidityZcfMint: ZCFMint}>} */
|
|
109
|
-
const addPool = async secondaryBrand => {
|
|
110
|
-
const liquidityZcfMint =
|
|
111
|
-
ammPowers.secondaryBrandToLiquidityMint.get(secondaryBrand);
|
|
112
|
-
|
|
113
|
-
const { zcfSeat: poolSeat } = ammPowers.zcf.makeEmptySeatKit();
|
|
114
|
-
const poolFacets = makePool(liquidityZcfMint, poolSeat, secondaryBrand);
|
|
115
|
-
|
|
116
|
-
ammPowers.secondaryBrandToPool.init(secondaryBrand, poolFacets);
|
|
117
|
-
return { liquidityZcfMint, poolFacets };
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
/** @param {ZCFSeat} seat */
|
|
121
|
-
const handleAddPoolOffer = async seat => {
|
|
122
|
-
assertProposalShape(seat, {
|
|
123
|
-
give: { Central: null, Secondary: null },
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
const {
|
|
127
|
-
give: { Central: centralAmount, Secondary: secondaryAmount },
|
|
128
|
-
want: proposalWant,
|
|
129
|
-
} = seat.getProposal();
|
|
130
|
-
const secondaryBrand = secondaryAmount.brand;
|
|
131
|
-
|
|
132
|
-
const { brand: liquidityBrand, issuer } =
|
|
133
|
-
ammPowers.secondaryBrandToLiquidityMint
|
|
134
|
-
.get(secondaryBrand)
|
|
135
|
-
.getIssuerRecord();
|
|
136
|
-
|
|
137
|
-
/** @type {Amount<'nat'>} */
|
|
138
|
-
// @ts-expect-error cast governance types
|
|
139
|
-
const minPoolLiquidity = ammPowers.params.getMinInitialPoolLiquidity();
|
|
140
|
-
|
|
141
|
-
if (proposalWant.Liquidity) {
|
|
142
|
-
const { Liquidity: wantLiquidityAmount } = proposalWant;
|
|
143
|
-
const centralAboveMinimum =
|
|
144
|
-
// @ts-expect-error central is NAT
|
|
145
|
-
centralAmount.value - minPoolLiquidity.value;
|
|
146
|
-
// when providing initial liquidity, the liquidity tokens issued will be
|
|
147
|
-
// equal to the central provided. Here, the reserve gets the minimum
|
|
148
|
-
const funderLiquidityAmount = AmountMath.make(
|
|
149
|
-
liquidityBrand,
|
|
150
|
-
centralAboveMinimum,
|
|
151
|
-
);
|
|
152
|
-
AmountMath.isGTE(funderLiquidityAmount, wantLiquidityAmount) ||
|
|
153
|
-
Fail`Requested too many liquidity tokens (${wantLiquidityAmount}, max: ${funderLiquidityAmount}`;
|
|
154
|
-
}
|
|
155
|
-
AmountMath.isGTE(centralAmount, minPoolLiquidity) ||
|
|
156
|
-
Fail`The minimum initial liquidity is ${minPoolLiquidity}, rejecting ${centralAmount}`;
|
|
157
|
-
const minLiqAmount = AmountMath.make(
|
|
158
|
-
liquidityBrand,
|
|
159
|
-
minPoolLiquidity.value,
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
// @ts-expect-error find might return undefined
|
|
163
|
-
const [liquidityKeyword] = Object.entries(zcf.getTerms().issuers).find(
|
|
164
|
-
([_, i]) => i === issuer,
|
|
165
|
-
);
|
|
166
|
-
assert(liquidityKeyword, 'liquidity brand required');
|
|
167
|
-
|
|
168
|
-
// COMMIT POINT /////////////////////
|
|
169
|
-
|
|
170
|
-
const {
|
|
171
|
-
poolFacets: { pool, helper },
|
|
172
|
-
} = await addPool(secondaryBrand);
|
|
173
|
-
|
|
174
|
-
// in addLiquidityInternal, funder provides centralAmount & secondaryAmount,
|
|
175
|
-
// and receives liquidity tokens equal to centralAmount. Afterward, we'll
|
|
176
|
-
// transfer minPoolLiquidity in tokens from the funder to the reserve.
|
|
177
|
-
helper.addLiquidityInternal(seat, secondaryAmount, centralAmount);
|
|
178
|
-
|
|
179
|
-
const { zcfSeat: reserveLiquidityTokenSeat } = zcf.makeEmptySeatKit();
|
|
180
|
-
atomicTransfer(
|
|
181
|
-
zcf,
|
|
182
|
-
seat,
|
|
183
|
-
reserveLiquidityTokenSeat,
|
|
184
|
-
{ Liquidity: minLiqAmount },
|
|
185
|
-
{ [liquidityKeyword]: minLiqAmount },
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
seat.exit();
|
|
189
|
-
pool.updateState();
|
|
190
|
-
|
|
191
|
-
await onOfferHandled(
|
|
192
|
-
secondaryBrand,
|
|
193
|
-
reserveLiquidityTokenSeat,
|
|
194
|
-
liquidityKeyword,
|
|
195
|
-
);
|
|
196
|
-
return 'Added liquidity.';
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
return () => zcf.makeInvitation(handleAddPoolOffer, 'Add Pool and Liquidity');
|
|
200
|
-
};
|
|
Binary file
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
# Constant Product AMM
|
|
2
|
-
|
|
3
|
-
A constant product automatic market maker based on our Ratio library. It charges
|
|
4
|
-
two kinds of fees: a pool fee remains in the pool to reward the liquidity
|
|
5
|
-
providers and a protocol fee is extracted to fund the economy. The external
|
|
6
|
-
entry point is a call to `pricesForStatedInput()` or `pricesForStatedOutput()`.
|
|
7
|
-
|
|
8
|
-
This algorithm uses the x*y=k formula directly, without fees. Briefly, there are
|
|
9
|
-
two kinds of assets, whose values are kept roughly in balance through the
|
|
10
|
-
actions of arbitrageurs. At any time a trader can trade with the pool by
|
|
11
|
-
offering to deposit one of the two assets. They will receive an amount
|
|
12
|
-
of the complementary asset that will maintain the invariant that the product of
|
|
13
|
-
the balances doesn't decrease. (Rounding is done in favor of the
|
|
14
|
-
pool.)
|
|
15
|
-
|
|
16
|
-
The user can specify a maximum amount they want to pay or a minimum amount they
|
|
17
|
-
want to receive. Unlike Uniswap, this approach will charge less than the user
|
|
18
|
-
offered or pay more than they asked for when appropriate. By analogy, if a user
|
|
19
|
-
is willing to pay up to $20 when the price of soda is $3 per bottle, it would
|
|
20
|
-
give 6 bottles and only charge $18. (We refer to these as "improved" prices.)
|
|
21
|
-
Uniswap doesn't adjust the provided price, so it would charge $20 in that trade.
|
|
22
|
-
This matters whenever the values of the smallest unit of the currencies are
|
|
23
|
-
significantly different, which is common in DeFi.
|
|
24
|
-
|
|
25
|
-
The rules that drive the design include
|
|
26
|
-
|
|
27
|
-
* When the user specifies an input (or output) price, they shouldn't pay more
|
|
28
|
-
(or receive less) than they said.
|
|
29
|
-
* The pool fee is charged against the side not specified by the user (the
|
|
30
|
-
"computed side"). This increases the pool balance without impacting the amount
|
|
31
|
-
that the user specified.
|
|
32
|
-
* The protocol fee is always charged in IST.
|
|
33
|
-
* The fees are calculated based on the pool balances before the transaction.
|
|
34
|
-
* Computations are rounded in favor of the pool.
|
|
35
|
-
|
|
36
|
-
We start by estimating the exchange rate, and calculate fees based on that. Once
|
|
37
|
-
we know the fees, we add or subtract them directly to the amounts added to and
|
|
38
|
-
extracted from the pools to adhere to those rules.
|
|
39
|
-
|
|
40
|
-
## Calculating fees
|
|
41
|
-
|
|
42
|
-
In these tables BLD represents any collateral. The user can specify how much
|
|
43
|
-
they want or how much they're willing to pay. We'll call the value they
|
|
44
|
-
specified **sGive** or **sGet** and bold it. We'll always refer to the currency
|
|
45
|
-
being added as X and the currency the user gets as Y. X and Y are the amounts
|
|
46
|
-
used to maintain the constant product invariant. On every transaction the pool
|
|
47
|
-
balance will also increase on one side or the other by the pool fee, and the
|
|
48
|
-
protocol fee will be extracted outside the pool. So the amount produced is less
|
|
49
|
-
(or the amount paid is more) because the invariant is maintained on the amounts
|
|
50
|
-
remaining after fees have been charged.
|
|
51
|
-
|
|
52
|
-
In the final table, we'll see what the user pays or gets (sGet, sGive), how much
|
|
53
|
-
the pool balances change (xIncr, yDecr) and the changes that impact the constant
|
|
54
|
-
product calculation(ΔX, ΔY). The amount paid and received by the
|
|
55
|
-
trader and changes to the pool are calculated relative to ΔX and ΔY
|
|
56
|
-
so that the pool grows by the poolFee and the protocolFee can be paid from the
|
|
57
|
-
proceeds.
|
|
58
|
-
|
|
59
|
-
This table shows which brands the amounts each have, as well as what is computed
|
|
60
|
-
vs. given. The PoolFee is computed based on the calculated amount (BLD in rows 1
|
|
61
|
-
and 2; IST in rows 3 and 4). The Protocol fee is always in IST.
|
|
62
|
-
|
|
63
|
-
| | In (X) | Out (Y) | PoolFee | Protocol Fee | Specified | Computed |
|
|
64
|
-
|---------|-----|-----|--------|-----|------|-----|
|
|
65
|
-
| **IST in** | IST | BLD | BLD | IST | **sGive** | sGet |
|
|
66
|
-
| **IST out** | BLD | IST | BLD | IST | **sGet** | sGive |
|
|
67
|
-
| **BLD in** | BLD | IST | IST | IST | **sGive** | sGet |
|
|
68
|
-
| **BLD out** | IST | BLD | IST | IST | **sGet** | sGive |
|
|
69
|
-
|
|
70
|
-
We'll calculate how much the pool balances would change in the no-fee, improved
|
|
71
|
-
price case using the constant product formulas. We call these results δX,
|
|
72
|
-
and δY. This table uses lower case δ to distinguish estimates from
|
|
73
|
-
final values, which use capital Δ.
|
|
74
|
-
|
|
75
|
-
The fees are based on δX, and δY. ρ is the poolFee (e.g., 30
|
|
76
|
-
basis points). φ is the protocol fee (e.g., 6 basis points). The pool fee
|
|
77
|
-
will be ρ times whichever of δX and δY was calculated. The
|
|
78
|
-
protocol fee will be φ * δX when IST is paid in, and φ * δY
|
|
79
|
-
when BLD is paid in. (α and β in this table are the no-fee versions.)
|
|
80
|
-
|
|
81
|
-
| | δX | δY | PoolFee | Protocol Fee |
|
|
82
|
-
|---------|-----|-----|--------|-----|
|
|
83
|
-
| **IST in** | **sGive** | y * α / (1 + α) | ρ × δY | φ × **sGive** (= φ × δX) |
|
|
84
|
-
| **IST out** | x * β / (1 - β) | **sGet** | ρ × δX | φ × **sGet** (= φ × δY) |
|
|
85
|
-
| **BLD in** | **sGive** | y * α / (1 + α) | ρ × δY | φ × δY |
|
|
86
|
-
| **BLD out** | x * β / (1 - β) | **sGet** | ρ × δX | φ × δX |
|
|
87
|
-
|
|
88
|
-
In rows 1 and 3, **sGive** was specified and sGet will be calculated. In rows 2
|
|
89
|
-
and 4, **sGet** was specified and sGive will be calculated. Once we know the
|
|
90
|
-
fees, we can add or subtract the fees and calculate the pool changes.
|
|
91
|
-
|
|
92
|
-
The next table shows how the amount added to and subtracted from the pool (xIncr
|
|
93
|
-
and yIncr) differs from what the trader gives or gets and the change in the pool
|
|
94
|
-
values. While X' and Y' are the values resulting from the constant product
|
|
95
|
-
formula, xHat and yHat are the new values in the pool, reflecting the
|
|
96
|
-
contribution of the pool fee. From the diagram, xIncr is the difference between
|
|
97
|
-
X and xHat, while yIncr is the difference between Y and yHat.
|
|
98
|
-
|
|
99
|
-
The protocol fee (φ) is always charged on the IST side, and doesn't go in
|
|
100
|
-
the pool. The pool fee (ρ) is charged on the amount that is calculated
|
|
101
|
-
(ΔX or ΔY) and is included in yHat.
|
|
102
|
-
|
|
103
|
-
We use the estimate of the amount in or out to calculate improved values of
|
|
104
|
-
ΔX and ΔY. These values tell us how much the trader will pay, the
|
|
105
|
-
changes in pool balances, and what the trader will receive. As before, ΔX
|
|
106
|
-
reflects a balance that will be growing, and ΔY one that will be
|
|
107
|
-
shrinking.
|
|
108
|
-
|
|
109
|
-

|
|
110
|
-
|
|
111
|
-
When IST is specified (in or out), we adjust by the protocol Fee to get the
|
|
112
|
-
change to the IST balance (which gives X' or Y'); use that to calculate the
|
|
113
|
-
BLD component of the constant product, and then adjust δ by the pool fee.
|
|
114
|
-
When BLD is specified, the entire amount will affect the pool balance (so the
|
|
115
|
-
value in the constant product formula is the final BLD balance.) Both the pool
|
|
116
|
-
and protocol fees are charged in IST so they respectively reduce sGet or
|
|
117
|
-
increase sGive.
|
|
118
|
-
|
|
119
|
-
This table shows how the pool's growth and trader's values differ from the
|
|
120
|
-
amounts that match the constant product formula. X is in IST on lines 1 and 4,
|
|
121
|
-
and BLD on lines 2 and 3.
|
|
122
|
-
|
|
123
|
-
| | xIncr | yDecr | pay In (sGive) | pay Out (sGet) |
|
|
124
|
-
|---------|-----|-----|-----|-----|
|
|
125
|
-
| **IST in** | ΔX | ΔY - PoolFee | ΔX + protocolFee | ΔY - PoolFee |
|
|
126
|
-
| **IST out** | ΔX + PoolFee | ΔY | ΔX + protocolFee + PoolFee | ΔY |
|
|
127
|
-
| **BLD in** | ΔX | ΔY - PoolFee | ΔX + ProtocolFee | ΔY - PoolFee |
|
|
128
|
-
| **BLD out** | ΔX + PoolFee | ΔY | ΔX + PoolFee + ProtocolFee | ΔY |
|
|
129
|
-
|
|
130
|
-
In the two right columns the protocolFee is either added to the amount the
|
|
131
|
-
trader pays, or subtracted from the proceeds. The poolFee does the same on the
|
|
132
|
-
left side, and it is either added to the amount deposited in the pool (xIncr)
|
|
133
|
-
or deducted from the amount removed from the pool (yDecr).
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
* line 1: the trader provides **sGive** IST; the pool will gain that minus
|
|
137
|
-
the protocol fee in IST. The value in the IST pool after (X') will be used to
|
|
138
|
-
calculate Y'. The pool fee is calculated from δY and the trader gets
|
|
139
|
-
&deltaY; - poolFee
|
|
140
|
-
* line 2: the trader asked for **sGet** IST; the pool will be reduced by that
|
|
141
|
-
amount plus the protocol fee. Y' will be used to calculate X'. The trader
|
|
142
|
-
must provide &deltaX; plus poolFee
|
|
143
|
-
* Line 3: the trader pays **sGive** BLD; the pool will increase by that minus
|
|
144
|
-
both fees
|
|
145
|
-
* line 4: the trader specified **sGet** BLD; the pool will be reduced by that
|
|
146
|
-
plus the protocol fee.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
## Example
|
|
150
|
-
|
|
151
|
-
For example, let's say the pool has 40,000,000 IST and 3,000,000 BLD. Alice
|
|
152
|
-
requests a swapIn with inputAmount of 30,000 IST, and outputAmount of 2000 BLD.
|
|
153
|
-
(SwapIn means the inputValue is the basis of the computation, while outputAmount
|
|
154
|
-
is treated as a minimum). To make the numbers concrete, we'll say the pool fee
|
|
155
|
-
is 25 Basis Points, and the protocol fee is 5 Basis Points.
|
|
156
|
-
|
|
157
|
-
The first step is to compute the trade that would take place with no fees. 30K
|
|
158
|
-
will be added to 40M IST. To keep the product just above 120MM, the BLD will be
|
|
159
|
-
reduced to 2,997,752.
|
|
160
|
-
|
|
161
|
-
```
|
|
162
|
-
40,030,000 * 2,997,752 > 40,000,000 * 3,000,000 > 40,030,000 * 2,997,751
|
|
163
|
-
120000012560000 > 120000000000000 > 119999972530000
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
But we get an even tighter bound by reducing the amount Alice has to spend
|
|
167
|
-
|
|
168
|
-
```
|
|
169
|
-
40,029,996 * 2,997,752 > 40,000,000 * 3,000,000 > 40,029,995 * 2,997,752
|
|
170
|
-
120000000568992 > 120000000000000 > 119999997571240
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
The initial price estimate is that 29,996 IST would get 2248 BLD in a no-fee
|
|
174
|
-
pool. We base fees on this estimate, so the **protocol Fee will be 15 IST**
|
|
175
|
-
(always in IST, ⌈29,996 * 0.0005⌉) and the **pool fee will be 6 BLD**
|
|
176
|
-
(in the computed side, ⌈2248 * 0.0025⌉). The "computed side" for
|
|
177
|
-
`swapIn` is output and for `swapOut` is input.
|
|
178
|
-
|
|
179
|
-
Now we calculate the actual ΔX and ΔY, since the fees affect the
|
|
180
|
-
size of the changes to the pool. From the first row of the third table we see
|
|
181
|
-
that the calculation starts from ΔX of
|
|
182
|
-
`sGive - ProtocolFee (i.e. 30,000 - 15 = 29,985)`
|
|
183
|
-
|
|
184
|
-
```
|
|
185
|
-
40,029,985 * 2,997,753 > 40,000,000 * 3,000,000 > 40,029,985 * 2,997,752
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
and knowing that 2,997,753 BLD must remain in the pool, we refine the required
|
|
189
|
-
amount of IST:
|
|
190
|
-
|
|
191
|
-
```
|
|
192
|
-
40,029,983 * 2,997,753 > 40,000,000 * 3,000,000 > 40,029,982 * 2,997,753
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
**ΔX is 29,983, and ΔY is 2247**.
|
|
196
|
-
|
|
197
|
-
* Alice pays ΔX + protocolFee, which is 29,983 + 15 (29998 IST)
|
|
198
|
-
* Alice will receive ΔY - PoolFee which is 2247 - 6 (2241 BLD)
|
|
199
|
-
* The IST in the pool will increase by ΔX (29983 IST)
|
|
200
|
-
* The BLD in the pool will decrease by ΔY (2247 BLD)
|
|
201
|
-
|
|
202
|
-
The Pool grew by 6 BLD more than was required to maintain the constant product
|
|
203
|
-
invariant. 15 IST were extracted for the protocol fee.
|
|
204
|
-
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { AmountMath } from '@agoric/ertp';
|
|
2
|
-
import {
|
|
3
|
-
ceilMultiplyBy,
|
|
4
|
-
makeRatio,
|
|
5
|
-
} from '@agoric/zoe/src/contractSupport/ratio.js';
|
|
6
|
-
|
|
7
|
-
import './types.js';
|
|
8
|
-
import './internal-types.js';
|
|
9
|
-
|
|
10
|
-
import { BASIS_POINTS } from './defaults.js';
|
|
11
|
-
|
|
12
|
-
const { Fail } = assert;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Make a ratio given a nat representing basis points and a brand.
|
|
16
|
-
*
|
|
17
|
-
* @type {MakeFeeRatio}
|
|
18
|
-
*/
|
|
19
|
-
const makeFeeRatio = (feeBP, brandOfFee) =>
|
|
20
|
-
makeRatio(feeBP, brandOfFee, BASIS_POINTS);
|
|
21
|
-
|
|
22
|
-
/** @type {Maximum} */
|
|
23
|
-
const maximum = (left, right) => {
|
|
24
|
-
// If left is greater or equal, return left. Otherwise return right.
|
|
25
|
-
return AmountMath.isGTE(left, right) ? left : right;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/** @type {AmountGT} */
|
|
29
|
-
const amountGT = (left, right) =>
|
|
30
|
-
AmountMath.isGTE(left, right) && !AmountMath.isEqual(left, right);
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Apply the feeRatio to the amount that has a matching brand. This used to
|
|
34
|
-
* calculate fees in the single pool case.
|
|
35
|
-
*
|
|
36
|
-
* @param {{ amountIn: Amount<'nat'>, amountOut: Amount<'nat'>}} amounts - a record with two
|
|
37
|
-
* amounts in different brands.
|
|
38
|
-
* @param {Ratio} feeRatio
|
|
39
|
-
* @returns {Amount<'nat'>}
|
|
40
|
-
*/
|
|
41
|
-
const calcFee = ({ amountIn, amountOut }, feeRatio) => {
|
|
42
|
-
feeRatio.numerator.brand === feeRatio.denominator.brand ||
|
|
43
|
-
Fail`feeRatio numerator and denominator must use the same brand ${feeRatio}`;
|
|
44
|
-
|
|
45
|
-
let sameBrandAmount;
|
|
46
|
-
if (amountIn.brand === feeRatio.numerator.brand) {
|
|
47
|
-
sameBrandAmount = amountIn;
|
|
48
|
-
} else if (amountOut.brand === feeRatio.numerator.brand) {
|
|
49
|
-
sameBrandAmount = amountOut;
|
|
50
|
-
} else {
|
|
51
|
-
throw Fail`feeRatio's brand (${feeRatio.numerator.brand}) must match one of the amounts [${amountIn}, ${amountOut}].`;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Always round fees up
|
|
55
|
-
const fee = ceilMultiplyBy(sameBrandAmount, feeRatio);
|
|
56
|
-
|
|
57
|
-
// Fee cannot exceed the amount on which it is levied
|
|
58
|
-
AmountMath.isGTE(sameBrandAmount, fee) ||
|
|
59
|
-
Fail`The feeRatio can't be greater than 1 ${feeRatio}`;
|
|
60
|
-
|
|
61
|
-
return fee;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Estimate the swap values, then calculate fees. The swapFn provided by the
|
|
66
|
-
* caller will be swapInNoFees or swapOutNoFees.
|
|
67
|
-
* SwapOut.
|
|
68
|
-
*
|
|
69
|
-
* @type {CalculateFees}
|
|
70
|
-
*/
|
|
71
|
-
const calculateFees = (
|
|
72
|
-
amountGiven,
|
|
73
|
-
poolAllocation,
|
|
74
|
-
amountWanted,
|
|
75
|
-
protocolFeeRatio,
|
|
76
|
-
poolFeeRatio,
|
|
77
|
-
swapFn,
|
|
78
|
-
) => {
|
|
79
|
-
// Get a rough estimation in both brands of the amount to be swapped
|
|
80
|
-
const estimation = swapFn({ amountGiven, poolAllocation, amountWanted });
|
|
81
|
-
|
|
82
|
-
const protocolFee = calcFee(estimation, protocolFeeRatio);
|
|
83
|
-
const poolFee = calcFee(estimation, poolFeeRatio);
|
|
84
|
-
|
|
85
|
-
return harden({ protocolFee, poolFee, ...estimation });
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
harden(amountGT);
|
|
89
|
-
harden(maximum);
|
|
90
|
-
harden(makeFeeRatio);
|
|
91
|
-
harden(calculateFees);
|
|
92
|
-
|
|
93
|
-
export { amountGT, maximum, makeFeeRatio, calculateFees };
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { Far } from '@endo/marshal';
|
|
2
|
-
|
|
3
|
-
import { swap } from './swap.js';
|
|
4
|
-
import { assertKInvariantSellingX } from './invariants.js';
|
|
5
|
-
import { getXY } from './getXY.js';
|
|
6
|
-
import { swapInNoFees, swapOutNoFees } from './core.js';
|
|
7
|
-
|
|
8
|
-
// pricesForStatedOutput() and pricesForStatedInput are the external entrypoints
|
|
9
|
-
// to the constantProduct module. The amountWanted is optional for
|
|
10
|
-
// pricesForStatedInput and amountgiven is optional for pricesForStatedOutput.
|
|
11
|
-
|
|
12
|
-
// The two methods call swap, passing in different functions for noFeeSwap.
|
|
13
|
-
// pricesForStatedInput uses swapInNoFees, while pricesForStatedOutput uses
|
|
14
|
-
// swapOutNoFees. the noFeesSwap functions
|
|
15
|
-
const makeCalcSwapPrices = noFeesSwap => {
|
|
16
|
-
return Far(
|
|
17
|
-
'calcSwapPrices',
|
|
18
|
-
(
|
|
19
|
-
amountGiven,
|
|
20
|
-
poolAllocation,
|
|
21
|
-
amountWanted,
|
|
22
|
-
protocolFeeRatio,
|
|
23
|
-
poolFeeRatio,
|
|
24
|
-
) => {
|
|
25
|
-
const result = swap(
|
|
26
|
-
amountGiven,
|
|
27
|
-
poolAllocation,
|
|
28
|
-
amountWanted,
|
|
29
|
-
protocolFeeRatio,
|
|
30
|
-
poolFeeRatio,
|
|
31
|
-
noFeesSwap,
|
|
32
|
-
);
|
|
33
|
-
const { x, y } = getXY({
|
|
34
|
-
amountGiven,
|
|
35
|
-
poolAllocation,
|
|
36
|
-
amountWanted,
|
|
37
|
-
});
|
|
38
|
-
assertKInvariantSellingX(x, y, result.xIncrement, result.yDecrement);
|
|
39
|
-
return result;
|
|
40
|
-
},
|
|
41
|
-
);
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
/** @type {CalcSwapInPrices} */
|
|
45
|
-
const pricesForStatedInput = (
|
|
46
|
-
amountGiven,
|
|
47
|
-
poolAllocation,
|
|
48
|
-
amountWanted,
|
|
49
|
-
protocolFeeRatio,
|
|
50
|
-
poolFeeRatio,
|
|
51
|
-
) => {
|
|
52
|
-
const calcSwapPrices = makeCalcSwapPrices(swapInNoFees);
|
|
53
|
-
return calcSwapPrices(
|
|
54
|
-
amountGiven,
|
|
55
|
-
poolAllocation,
|
|
56
|
-
amountWanted,
|
|
57
|
-
protocolFeeRatio,
|
|
58
|
-
poolFeeRatio,
|
|
59
|
-
);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/** @type {CalcSwapOutPrices} */
|
|
63
|
-
const pricesForStatedOutput = (
|
|
64
|
-
amountGiven,
|
|
65
|
-
poolAllocation,
|
|
66
|
-
amountWanted,
|
|
67
|
-
protocolFeeRatio,
|
|
68
|
-
poolFeeRatio,
|
|
69
|
-
) => {
|
|
70
|
-
const calcSwapPrices = makeCalcSwapPrices(swapOutNoFees);
|
|
71
|
-
return calcSwapPrices(
|
|
72
|
-
amountGiven,
|
|
73
|
-
poolAllocation,
|
|
74
|
-
amountWanted,
|
|
75
|
-
protocolFeeRatio,
|
|
76
|
-
poolFeeRatio,
|
|
77
|
-
);
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
harden(pricesForStatedInput);
|
|
81
|
-
harden(pricesForStatedOutput);
|
|
82
|
-
|
|
83
|
-
export { pricesForStatedOutput, pricesForStatedInput };
|