@agoric/fast-usdc 0.2.0-upgrade-19-dev-0754752.0 → 0.2.0-upgrade-18a-dev-4ee0508.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/package.json +25 -26
- package/src/cli/cli.js +1 -1
- package/src/cli/config-commands.js +1 -1
- package/src/cli/config.js +1 -1
- package/src/cli/lp-commands.js +39 -21
- package/src/cli/operator-commands.js +0 -1
- package/src/cli/transfer.js +5 -5
- package/src/exos/advancer.js +41 -109
- package/src/exos/liquidity-pool.js +34 -81
- package/src/exos/operator-kit.js +2 -0
- package/src/exos/settler.js +95 -154
- package/src/exos/status-manager.js +59 -97
- package/src/exos/transaction-feed.js +9 -30
- package/src/fast-usdc-policy.core.js +12 -2
- package/src/fast-usdc.contract.js +31 -23
- package/src/{start-fast-usdc.core.js → fast-usdc.start.js} +81 -13
- package/src/pool-share-math.js +9 -55
- package/src/type-guards.js +8 -37
- package/src/types.ts +5 -34
- package/src/utils/deploy-config.js +75 -36
- package/src/utils/fees.js +4 -3
- package/src/add-operators.core.js +0 -63
- package/src/clientSupport.js +0 -98
- package/src/distribute-fees.core.js +0 -93
- package/src/main.js +0 -1
- package/src/utils/chain-policies.js +0 -140
- package/src/utils/core-eval.js +0 -73
- /package/src/{cli/util → util}/agoric.js +0 -0
- /package/src/{cli/util → util}/bank.js +0 -0
- /package/src/{cli/util → util}/cctp.js +0 -0
- /package/src/{cli/util → util}/file.js +0 -0
- /package/src/{cli/util → util}/noble.js +0 -0
|
@@ -1,22 +1,30 @@
|
|
|
1
|
-
import { deeplyFulfilledObject, makeTracer } from '@agoric/internal';
|
|
1
|
+
import { deeplyFulfilledObject, makeTracer, objectMap } from '@agoric/internal';
|
|
2
|
+
import {
|
|
3
|
+
CosmosChainInfoShape,
|
|
4
|
+
DenomDetailShape,
|
|
5
|
+
DenomShape,
|
|
6
|
+
} from '@agoric/orchestration';
|
|
7
|
+
import { Fail } from '@endo/errors';
|
|
2
8
|
import { E } from '@endo/far';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
9
|
+
import { makeMarshal } from '@endo/marshal';
|
|
10
|
+
import { M } from '@endo/patterns';
|
|
5
11
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from './
|
|
12
|
+
FastUSDCTermsShape,
|
|
13
|
+
FeeConfigShape,
|
|
14
|
+
FeedPolicyShape,
|
|
15
|
+
} from './type-guards.js';
|
|
16
|
+
import { fromExternalConfig } from './utils/config-marshal.js';
|
|
10
17
|
|
|
11
18
|
/**
|
|
12
|
-
* @import {
|
|
19
|
+
* @import {DepositFacet} from '@agoric/ertp/src/types.js'
|
|
20
|
+
* @import {TypedPattern} from '@agoric/internal'
|
|
13
21
|
* @import {Instance, StartParams} from '@agoric/zoe/src/zoeService/utils'
|
|
14
22
|
* @import {Board} from '@agoric/vats'
|
|
15
23
|
* @import {ManifestBundleRef} from '@agoric/deploy-script-support/src/externalTypes.js'
|
|
16
24
|
* @import {BootstrapManifest} from '@agoric/vats/src/core/lib-boot.js'
|
|
17
25
|
* @import {LegibleCapData} from './utils/config-marshal.js'
|
|
18
26
|
* @import {FastUsdcSF} from './fast-usdc.contract.js'
|
|
19
|
-
* @import {FastUSDCConfig} from './types.js'
|
|
27
|
+
* @import {FeedPolicy, FastUSDCConfig} from './types.js'
|
|
20
28
|
*/
|
|
21
29
|
|
|
22
30
|
const ShareAssetInfo = /** @type {const} */ harden({
|
|
@@ -30,6 +38,16 @@ const trace = makeTracer('FUSD-Start', true);
|
|
|
30
38
|
|
|
31
39
|
const contractName = 'fastUsdc';
|
|
32
40
|
|
|
41
|
+
/** @type {TypedPattern<FastUSDCConfig>} */
|
|
42
|
+
export const FastUSDCConfigShape = M.splitRecord({
|
|
43
|
+
terms: FastUSDCTermsShape,
|
|
44
|
+
oracles: M.recordOf(M.string(), M.string()),
|
|
45
|
+
feeConfig: FeeConfigShape,
|
|
46
|
+
feedPolicy: FeedPolicyShape,
|
|
47
|
+
chainInfo: M.recordOf(M.string(), CosmosChainInfoShape),
|
|
48
|
+
assetInfo: M.arrayOf([DenomShape, DenomDetailShape]),
|
|
49
|
+
});
|
|
50
|
+
|
|
33
51
|
/**
|
|
34
52
|
* XXX Shouldn't the bridge or board vat handle this?
|
|
35
53
|
*
|
|
@@ -46,8 +64,38 @@ const makePublishingStorageKit = async (path, { chainStorage, board }) => {
|
|
|
46
64
|
return { storageNode, marshaller };
|
|
47
65
|
};
|
|
48
66
|
|
|
67
|
+
const BOARD_AUX = 'boardAux';
|
|
68
|
+
const marshalData = makeMarshal(_val => Fail`data only`);
|
|
69
|
+
/**
|
|
70
|
+
* @param {Brand} brand
|
|
71
|
+
* @param {Pick<BootstrapPowers['consume'], 'board' | 'chainStorage'>} powers
|
|
72
|
+
*/
|
|
73
|
+
const publishDisplayInfo = async (brand, { board, chainStorage }) => {
|
|
74
|
+
// chainStorage type includes undefined, which doesn't apply here.
|
|
75
|
+
// @ts-expect-error UNTIL https://github.com/Agoric/agoric-sdk/issues/8247
|
|
76
|
+
const boardAux = E(chainStorage).makeChildNode(BOARD_AUX);
|
|
77
|
+
const [id, displayInfo, allegedName] = await Promise.all([
|
|
78
|
+
E(board).getId(brand),
|
|
79
|
+
E(brand).getDisplayInfo(),
|
|
80
|
+
E(brand).getAllegedName(),
|
|
81
|
+
]);
|
|
82
|
+
const node = E(boardAux).makeChildNode(id);
|
|
83
|
+
const aux = marshalData.toCapData(harden({ allegedName, displayInfo }));
|
|
84
|
+
await E(node).setValue(JSON.stringify(aux));
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const FEED_POLICY = 'feedPolicy';
|
|
49
88
|
const POOL_METRICS = 'poolMetrics';
|
|
50
89
|
|
|
90
|
+
/**
|
|
91
|
+
* @param {ERef<StorageNode>} node
|
|
92
|
+
* @param {FeedPolicy} policy
|
|
93
|
+
*/
|
|
94
|
+
const publishFeedPolicy = async (node, policy) => {
|
|
95
|
+
const feedPolicy = E(node).makeChildNode(FEED_POLICY);
|
|
96
|
+
await E(feedPolicy).setValue(JSON.stringify(policy));
|
|
97
|
+
};
|
|
98
|
+
|
|
51
99
|
/**
|
|
52
100
|
* @typedef { PromiseSpaceOf<{
|
|
53
101
|
* fastUsdcKit: FastUSDCKit
|
|
@@ -59,7 +107,6 @@ const POOL_METRICS = 'poolMetrics';
|
|
|
59
107
|
* }} FastUSDCCorePowers
|
|
60
108
|
*
|
|
61
109
|
* @typedef {StartedInstanceKitWithLabel & {
|
|
62
|
-
* creatorFacet: StartedInstanceKit<FastUsdcSF>['creatorFacet'];
|
|
63
110
|
* privateArgs: StartParams<FastUsdcSF>['privateArgs'];
|
|
64
111
|
* }} FastUSDCKit
|
|
65
112
|
*/
|
|
@@ -117,6 +164,18 @@ export const startFastUSDC = async (
|
|
|
117
164
|
trace('using terms', terms);
|
|
118
165
|
trace('using fee config', feeConfig);
|
|
119
166
|
|
|
167
|
+
trace('look up oracle deposit facets');
|
|
168
|
+
const oracleDepositFacets = await deeplyFulfilledObject(
|
|
169
|
+
objectMap(oracles, async address => {
|
|
170
|
+
/** @type {DepositFacet} */
|
|
171
|
+
const depositFacet = await E(namesByAddress).lookup(
|
|
172
|
+
address,
|
|
173
|
+
'depositFacet',
|
|
174
|
+
);
|
|
175
|
+
return depositFacet;
|
|
176
|
+
}),
|
|
177
|
+
);
|
|
178
|
+
|
|
120
179
|
const { storageNode, marshaller } = await makePublishingStorageKit(
|
|
121
180
|
contractName,
|
|
122
181
|
{
|
|
@@ -172,7 +231,16 @@ export const startFastUSDC = async (
|
|
|
172
231
|
brand: shareBrand,
|
|
173
232
|
});
|
|
174
233
|
|
|
175
|
-
await
|
|
234
|
+
await Promise.all(
|
|
235
|
+
Object.entries(oracleDepositFacets).map(async ([name, depositFacet]) => {
|
|
236
|
+
const address = oracles[name];
|
|
237
|
+
trace('making invitation for', name, address);
|
|
238
|
+
const toWatch = await E(creatorFacet).makeOperatorInvitation(address);
|
|
239
|
+
|
|
240
|
+
const amt = await E(depositFacet).receive(toWatch);
|
|
241
|
+
trace('sent', amt, 'to', name);
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
176
244
|
|
|
177
245
|
produceInstance.reset();
|
|
178
246
|
produceInstance.resolve(instance);
|
|
@@ -225,10 +293,10 @@ export const getManifestForFastUSDC = (
|
|
|
225
293
|
board: true,
|
|
226
294
|
},
|
|
227
295
|
issuer: {
|
|
228
|
-
produce: { FastLP: true },
|
|
296
|
+
produce: { FastLP: true }, // UNTIL #10432
|
|
229
297
|
},
|
|
230
298
|
brand: {
|
|
231
|
-
produce: { FastLP: true },
|
|
299
|
+
produce: { FastLP: true }, // UNTIL #10432
|
|
232
300
|
},
|
|
233
301
|
instance: {
|
|
234
302
|
produce: { fastUsdc: true },
|
package/src/pool-share-math.js
CHANGED
|
@@ -7,11 +7,9 @@ import {
|
|
|
7
7
|
} from '@agoric/zoe/src/contractSupport/ratio.js';
|
|
8
8
|
import { Fail, q } from '@endo/errors';
|
|
9
9
|
|
|
10
|
-
const {
|
|
11
|
-
const { add, isEmpty, isEqual, isGTE, make, makeEmpty, subtract } = AmountMath;
|
|
10
|
+
const { getValue, add, isEmpty, isEqual, isGTE, subtract } = AmountMath;
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
|
-
* @import {Amount, Brand, DepositFacet, NatValue, Payment} from '@agoric/ertp';
|
|
15
13
|
* @import {PoolStats} from './types';
|
|
16
14
|
* @import {RepayAmountKWR} from './exos/liquidity-pool';
|
|
17
15
|
*/
|
|
@@ -19,7 +17,8 @@ const { add, isEmpty, isEqual, isGTE, make, makeEmpty, subtract } = AmountMath;
|
|
|
19
17
|
/**
|
|
20
18
|
* Invariant: shareWorth is the pool balance divided by shares outstanding.
|
|
21
19
|
*
|
|
22
|
-
* Use `makeParity(USDC, PoolShares)` for an initial
|
|
20
|
+
* Use `makeParity(make(USDC, epsilon), PoolShares)` for an initial
|
|
21
|
+
* value, for some negligible `epsilon` such as 1n.
|
|
23
22
|
*
|
|
24
23
|
* @typedef {Ratio} ShareWorth
|
|
25
24
|
*/
|
|
@@ -27,26 +26,23 @@ const { add, isEmpty, isEqual, isGTE, make, makeEmpty, subtract } = AmountMath;
|
|
|
27
26
|
/**
|
|
28
27
|
* Make a 1-to-1 ratio between amounts of 2 brands.
|
|
29
28
|
*
|
|
30
|
-
* @param {
|
|
29
|
+
* @param {Amount<'nat'>} numerator
|
|
31
30
|
* @param {Brand<'nat'>} denominatorBrand
|
|
32
31
|
*/
|
|
33
|
-
export const makeParity = (
|
|
34
|
-
const
|
|
35
|
-
return makeRatio(
|
|
32
|
+
export const makeParity = (numerator, denominatorBrand) => {
|
|
33
|
+
const value = getValue(numerator.brand, numerator);
|
|
34
|
+
return makeRatio(value, numerator.brand, value, denominatorBrand);
|
|
36
35
|
};
|
|
37
36
|
|
|
38
37
|
/**
|
|
39
38
|
* @typedef {{
|
|
40
39
|
* deposit: {
|
|
41
40
|
* give: { USDC: Amount<'nat'> },
|
|
42
|
-
* want
|
|
41
|
+
* want?: { PoolShare: Amount<'nat'> }
|
|
43
42
|
* },
|
|
44
43
|
* withdraw: {
|
|
45
44
|
* give: { PoolShare: Amount<'nat'> }
|
|
46
45
|
* want: { USDC: Amount<'nat'> },
|
|
47
|
-
* },
|
|
48
|
-
* withdrawFees: {
|
|
49
|
-
* want: { USDC: Amount<'nat'> }
|
|
50
46
|
* }
|
|
51
47
|
* }} USDCProposalShapes
|
|
52
48
|
*/
|
|
@@ -95,54 +91,14 @@ export const depositCalc = (shareWorth, { give, want }) => {
|
|
|
95
91
|
});
|
|
96
92
|
};
|
|
97
93
|
|
|
98
|
-
/**
|
|
99
|
-
* Verifies that the total pool balance (unencumbered + encumbered) matches the
|
|
100
|
-
* shareWorth numerator. The total pool balance consists of:
|
|
101
|
-
* 1. unencumbered balance - USDC available in the pool for borrowing
|
|
102
|
-
* 2. encumbered balance - USDC currently lent out
|
|
103
|
-
*
|
|
104
|
-
* A negligible `dust` amount is used to initialize shareWorth with a non-zero
|
|
105
|
-
* denominator. It must remain in the pool at all times.
|
|
106
|
-
*
|
|
107
|
-
* @param {Allocation} poolAlloc
|
|
108
|
-
* @param {ShareWorth} shareWorth
|
|
109
|
-
* @param {Amount<'nat'>} encumberedBalance
|
|
110
|
-
*/
|
|
111
|
-
export const checkPoolBalance = (poolAlloc, shareWorth, encumberedBalance) => {
|
|
112
|
-
const { brand: usdcBrand } = encumberedBalance;
|
|
113
|
-
const unencumberedBalance = poolAlloc.USDC || makeEmpty(usdcBrand);
|
|
114
|
-
const kwds = keys(poolAlloc);
|
|
115
|
-
kwds.length === 0 ||
|
|
116
|
-
(kwds.length === 1 && kwds[0] === 'USDC') ||
|
|
117
|
-
Fail`unexpected pool allocations: ${poolAlloc}`;
|
|
118
|
-
const dust = make(usdcBrand, 1n);
|
|
119
|
-
const grossBalance = add(add(unencumberedBalance, dust), encumberedBalance);
|
|
120
|
-
isEqual(grossBalance, shareWorth.numerator) ||
|
|
121
|
-
Fail`🚨 pool balance ${q(unencumberedBalance)} and encumbered balance ${q(encumberedBalance)} inconsistent with shareWorth ${q(shareWorth)}`;
|
|
122
|
-
return harden({ unencumberedBalance, grossBalance });
|
|
123
|
-
};
|
|
124
|
-
|
|
125
94
|
/**
|
|
126
95
|
* Compute payout from a withdraw proposal, along with updated shareWorth
|
|
127
96
|
*
|
|
128
97
|
* @param {ShareWorth} shareWorth
|
|
129
98
|
* @param {USDCProposalShapes['withdraw']} proposal
|
|
130
|
-
* @param {Allocation} poolAlloc
|
|
131
|
-
* @param {Amount<'nat'>} [encumberedBalance]
|
|
132
99
|
* @returns {{ shareWorth: ShareWorth, payouts: { USDC: Amount<'nat'> }}}
|
|
133
100
|
*/
|
|
134
|
-
export const withdrawCalc = (
|
|
135
|
-
shareWorth,
|
|
136
|
-
{ give, want },
|
|
137
|
-
poolAlloc,
|
|
138
|
-
encumberedBalance = makeEmpty(shareWorth.numerator.brand),
|
|
139
|
-
) => {
|
|
140
|
-
const { unencumberedBalance } = checkPoolBalance(
|
|
141
|
-
poolAlloc,
|
|
142
|
-
shareWorth,
|
|
143
|
-
encumberedBalance,
|
|
144
|
-
);
|
|
145
|
-
|
|
101
|
+
export const withdrawCalc = (shareWorth, { give, want }) => {
|
|
146
102
|
assert(!isEmpty(give.PoolShare));
|
|
147
103
|
assert(!isEmpty(want.USDC));
|
|
148
104
|
|
|
@@ -152,8 +108,6 @@ export const withdrawCalc = (
|
|
|
152
108
|
const { denominator: sharesOutstanding, numerator: poolBalance } = shareWorth;
|
|
153
109
|
!isGTE(want.USDC, poolBalance) ||
|
|
154
110
|
Fail`cannot withdraw ${q(want.USDC)}; only ${q(poolBalance)} in pool`;
|
|
155
|
-
isGTE(unencumberedBalance, want.USDC) ||
|
|
156
|
-
Fail`cannot withdraw ${q(want.USDC)}; ${q(encumberedBalance)} is in use; stand by for pool to return to ${q(poolBalance)}`;
|
|
157
111
|
const balancePost = subtract(poolBalance, payout);
|
|
158
112
|
// giving more shares than are outstanding is impossible,
|
|
159
113
|
// so it's not worth a custom diagnostic. subtract will fail
|
package/src/type-guards.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import { AmountShape, BrandShape, RatioShape } from '@agoric/ertp';
|
|
2
2
|
import { M } from '@endo/patterns';
|
|
3
|
-
import {
|
|
4
|
-
CosmosChainInfoShape,
|
|
5
|
-
DenomDetailShape,
|
|
6
|
-
DenomShape,
|
|
7
|
-
} from '@agoric/orchestration/src/typeGuards.js';
|
|
8
3
|
import { PendingTxStatus } from './constants.js';
|
|
9
4
|
|
|
10
5
|
/**
|
|
11
|
-
* @import {Amount, Brand, NatValue, Payment} from '@agoric/ertp';
|
|
12
6
|
* @import {TypedPattern} from '@agoric/internal';
|
|
13
7
|
* @import {FastUsdcTerms} from './fast-usdc.contract.js';
|
|
14
8
|
* @import {USDCProposalShapes} from './pool-share-math.js';
|
|
15
|
-
* @import {CctpTxEvidence,
|
|
9
|
+
* @import {CctpTxEvidence, FeeConfig, PendingTx, PoolMetrics, ChainPolicy, FeedPolicy, AddressHook, EvmAddress, EvmHash, RiskAssessment, EvidenceWithRisk} from './types.js';
|
|
16
10
|
*/
|
|
17
11
|
|
|
18
12
|
/**
|
|
@@ -25,20 +19,16 @@ export const makeNatAmountShape = (brand, min) =>
|
|
|
25
19
|
/** @param {Record<'PoolShares' | 'USDC', Brand<'nat'>>} brands */
|
|
26
20
|
export const makeProposalShapes = ({ PoolShares, USDC }) => {
|
|
27
21
|
/** @type {TypedPattern<USDCProposalShapes['deposit']>} */
|
|
28
|
-
const deposit = M.splitRecord(
|
|
29
|
-
give: { USDC: makeNatAmountShape(USDC, 1n) },
|
|
30
|
-
want: { PoolShare: makeNatAmountShape(PoolShares) },
|
|
31
|
-
|
|
22
|
+
const deposit = M.splitRecord(
|
|
23
|
+
{ give: { USDC: makeNatAmountShape(USDC, 1n) } },
|
|
24
|
+
{ want: M.splitRecord({}, { PoolShare: makeNatAmountShape(PoolShares) }) },
|
|
25
|
+
);
|
|
32
26
|
/** @type {TypedPattern<USDCProposalShapes['withdraw']>} */
|
|
33
27
|
const withdraw = M.splitRecord({
|
|
34
28
|
give: { PoolShare: makeNatAmountShape(PoolShares, 1n) },
|
|
35
29
|
want: { USDC: makeNatAmountShape(USDC, 1n) },
|
|
36
30
|
});
|
|
37
|
-
|
|
38
|
-
const withdrawFees = M.splitRecord({
|
|
39
|
-
want: { USDC: makeNatAmountShape(USDC, 1n) },
|
|
40
|
-
});
|
|
41
|
-
return harden({ deposit, withdraw, withdrawFees });
|
|
31
|
+
return harden({ deposit, withdraw });
|
|
42
32
|
};
|
|
43
33
|
|
|
44
34
|
/** @type {TypedPattern<FastUsdcTerms>} */
|
|
@@ -76,7 +66,6 @@ export const CctpTxEvidenceShape = {
|
|
|
76
66
|
},
|
|
77
67
|
blockHash: EvmHashShape,
|
|
78
68
|
blockNumber: M.nat(),
|
|
79
|
-
blockTimestamp: M.nat(),
|
|
80
69
|
chainId: M.number(),
|
|
81
70
|
tx: {
|
|
82
71
|
amount: M.nat(),
|
|
@@ -114,6 +103,7 @@ const NatAmountShape = { brand: BrandShape, value: M.nat() };
|
|
|
114
103
|
export const FeeConfigShape = {
|
|
115
104
|
flat: NatAmountShape,
|
|
116
105
|
variableRate: RatioShape,
|
|
106
|
+
maxVariable: NatAmountShape,
|
|
117
107
|
contractRate: RatioShape,
|
|
118
108
|
};
|
|
119
109
|
harden(FeeConfigShape);
|
|
@@ -131,19 +121,10 @@ harden(PoolMetricsShape);
|
|
|
131
121
|
|
|
132
122
|
/** @type {TypedPattern<ChainPolicy>} */
|
|
133
123
|
export const ChainPolicyShape = {
|
|
134
|
-
|
|
135
|
-
[EvmHashShape],
|
|
136
|
-
undefined,
|
|
137
|
-
M.arrayOf(EvmHashShape),
|
|
138
|
-
),
|
|
124
|
+
attenuatedCttpBridgeAddress: EvmHashShape,
|
|
139
125
|
cctpTokenMessengerAddress: EvmHashShape,
|
|
140
126
|
confirmations: M.number(),
|
|
141
127
|
chainId: M.number(),
|
|
142
|
-
rateLimits: {
|
|
143
|
-
tx: M.bigint(),
|
|
144
|
-
blockWindow: M.bigint(),
|
|
145
|
-
blockWindowSize: M.number(),
|
|
146
|
-
},
|
|
147
128
|
};
|
|
148
129
|
harden(ChainPolicyShape);
|
|
149
130
|
|
|
@@ -162,13 +143,3 @@ export const FeedPolicyShape = M.splitRecord(
|
|
|
162
143
|
{ eventFilter: M.string() },
|
|
163
144
|
);
|
|
164
145
|
harden(FeedPolicyShape);
|
|
165
|
-
|
|
166
|
-
/** @type {TypedPattern<FastUSDCConfig>} */
|
|
167
|
-
export const FastUSDCConfigShape = M.splitRecord({
|
|
168
|
-
terms: FastUSDCTermsShape,
|
|
169
|
-
oracles: M.recordOf(M.string(), M.string()),
|
|
170
|
-
feeConfig: FeeConfigShape,
|
|
171
|
-
feedPolicy: FeedPolicyShape,
|
|
172
|
-
chainInfo: M.recordOf(M.string(), CosmosChainInfoShape),
|
|
173
|
-
assetInfo: M.arrayOf([DenomShape, DenomDetailShape]),
|
|
174
|
-
});
|
package/src/types.ts
CHANGED
|
@@ -27,21 +27,8 @@ export interface CctpTxEvidence {
|
|
|
27
27
|
forwardingChannel: IBCChannelID;
|
|
28
28
|
recipientAddress: ChainAddress['value'];
|
|
29
29
|
};
|
|
30
|
-
/** on the source chain (e.g. L1 Ethereum and L2s Arbitrum, Base) */
|
|
31
30
|
blockHash: EvmHash;
|
|
32
|
-
/** height of blockHash on the source chain */
|
|
33
31
|
blockNumber: bigint;
|
|
34
|
-
/**
|
|
35
|
-
* Seconds since Unix epoch. Not all CCTP source chains update time the same
|
|
36
|
-
* way but they all use Unix epoch and thus are approximately equal. (Within
|
|
37
|
-
* minutes apart.)
|
|
38
|
-
*/
|
|
39
|
-
blockTimestamp: bigint;
|
|
40
|
-
//
|
|
41
|
-
/**
|
|
42
|
-
* [Domain of values](https://chainid.network/) per [EIP-155](https://eips.ethereum.org/EIPS/eip-155)
|
|
43
|
-
* (We don't have [CCTP `domain`](https://developers.circle.com/stablecoins/supported-domains) )
|
|
44
|
-
*/
|
|
45
32
|
chainId: number;
|
|
46
33
|
/** data covered by signature (aka txHash) */
|
|
47
34
|
tx: {
|
|
@@ -68,12 +55,6 @@ export interface TransactionRecord extends CopyRecord {
|
|
|
68
55
|
status: TxStatus;
|
|
69
56
|
}
|
|
70
57
|
|
|
71
|
-
/** the record in vstorage at the path of the contract's node */
|
|
72
|
-
export interface ContractRecord extends CopyRecord {
|
|
73
|
-
poolAccount: ChainAddress['value'];
|
|
74
|
-
settlementAccount: ChainAddress['value'];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
58
|
export type LogFn = (...args: unknown[]) => void;
|
|
78
59
|
|
|
79
60
|
export interface PendingTx extends CctpTxEvidence {
|
|
@@ -81,11 +62,9 @@ export interface PendingTx extends CctpTxEvidence {
|
|
|
81
62
|
}
|
|
82
63
|
|
|
83
64
|
export type FeeConfig = {
|
|
84
|
-
/** flat fee charged for every advance */
|
|
85
65
|
flat: Amount<'nat'>;
|
|
86
|
-
/** proportion of advance kept as a fee */
|
|
87
66
|
variableRate: Ratio;
|
|
88
|
-
|
|
67
|
+
maxVariable: Amount<'nat'>;
|
|
89
68
|
contractRate: Ratio;
|
|
90
69
|
};
|
|
91
70
|
|
|
@@ -103,35 +82,27 @@ export interface PoolMetrics extends PoolStats {
|
|
|
103
82
|
|
|
104
83
|
export interface ChainPolicy {
|
|
105
84
|
/** `msg.sender` of DepositAndBurn to TokenMessenger must be an attenuated wrapper contract that does not contain `replaceDepositForBurn` */
|
|
106
|
-
|
|
85
|
+
attenuatedCttpBridgeAddress: EvmHash;
|
|
107
86
|
/** @see {@link https://developers.circle.com/stablecoins/evm-smart-contracts} */
|
|
108
87
|
cctpTokenMessengerAddress: EvmHash;
|
|
109
88
|
/** e.g., `1` for ETH mainnet 42161 for Arbitrum One. @see {@link https://chainlist.org/} */
|
|
110
89
|
chainId: EvmChainID;
|
|
111
90
|
/** the number of block confirmations to observe before reporting */
|
|
112
91
|
confirmations: number;
|
|
113
|
-
rateLimits: {
|
|
114
|
-
/** do not advance more than this amount for an individual transaction */
|
|
115
|
-
tx: bigint;
|
|
116
|
-
/** do not advance more than this amount per block window */
|
|
117
|
-
blockWindow: bigint;
|
|
118
|
-
/** the number of blocks to consider for `blockWindow` */
|
|
119
|
-
blockWindowSize: number;
|
|
120
|
-
};
|
|
121
92
|
}
|
|
122
93
|
|
|
123
|
-
export
|
|
94
|
+
export interface FeedPolicy {
|
|
124
95
|
nobleDomainId: number;
|
|
125
96
|
nobleAgoricChannelId: string;
|
|
126
97
|
chainPolicies: Record<EvmChainName, ChainPolicy>;
|
|
127
98
|
eventFilter?: string;
|
|
128
|
-
}
|
|
99
|
+
}
|
|
129
100
|
|
|
130
101
|
export type FastUSDCConfig = {
|
|
131
102
|
terms: FastUsdcTerms;
|
|
132
103
|
oracles: Record<string, string>;
|
|
133
104
|
feeConfig: FeeConfig;
|
|
134
|
-
feedPolicy: FeedPolicy;
|
|
105
|
+
feedPolicy: FeedPolicy & Passable;
|
|
135
106
|
noNoble: boolean; // support a3p-integration, which has no noble chain
|
|
136
107
|
chainInfo: Record<string, CosmosChainInfo & Passable>;
|
|
137
108
|
assetInfo: [Denom, DenomDetail & { brandKey?: string }][];
|
|
@@ -1,38 +1,50 @@
|
|
|
1
1
|
import { denomHash, withChainCapabilities } from '@agoric/orchestration';
|
|
2
2
|
import fetchedChainInfo from '@agoric/orchestration/src/fetched-chain-info.js';
|
|
3
|
-
import { ChainPolicies } from './chain-policies.js';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
|
-
* @import {FastUSDCConfig} from '@agoric/fast-usdc'
|
|
5
|
+
* @import {FastUSDCConfig} from '@agoric/fast-usdc/src/types.js'
|
|
7
6
|
* @import {Passable} from '@endo/marshal';
|
|
8
7
|
* @import {CosmosChainInfo, Denom, DenomDetail} from '@agoric/orchestration';
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
|
-
/** @type {[Denom, DenomDetail & { brandKey?: string}]} */
|
|
12
|
-
const usdcOnAgoric = [
|
|
13
|
-
`ibc/${denomHash({ denom: 'uusdc', channelId: fetchedChainInfo.agoric.connections['noble-1'].transferChannel.channelId })}`,
|
|
14
|
-
{
|
|
15
|
-
baseName: 'noble',
|
|
16
|
-
chainName: 'agoric',
|
|
17
|
-
baseDenom: 'uusdc',
|
|
18
|
-
brandKey: 'USDC',
|
|
19
|
-
},
|
|
20
|
-
];
|
|
21
|
-
|
|
22
10
|
/** @type {[Denom, DenomDetail & { brandKey?: string}][]} */
|
|
23
|
-
export const
|
|
24
|
-
[
|
|
25
|
-
|
|
11
|
+
export const defaultAssetInfo = [
|
|
12
|
+
[
|
|
13
|
+
'uusdc',
|
|
14
|
+
{
|
|
15
|
+
baseName: 'noble',
|
|
16
|
+
chainName: 'noble',
|
|
17
|
+
baseDenom: 'uusdc',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
[
|
|
21
|
+
`ibc/${denomHash({ denom: 'uusdc', channelId: fetchedChainInfo.agoric.connections['noble-1'].transferChannel.channelId })}`,
|
|
22
|
+
{
|
|
23
|
+
baseName: 'noble',
|
|
24
|
+
chainName: 'agoric',
|
|
25
|
+
baseDenom: 'uusdc',
|
|
26
|
+
brandKey: 'USDC',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
`ibc/${denomHash({ denom: 'uusdc', channelId: fetchedChainInfo.osmosis.connections['noble-1'].transferChannel.channelId })}`,
|
|
31
|
+
{
|
|
32
|
+
baseName: 'noble',
|
|
33
|
+
chainName: 'osmosis',
|
|
34
|
+
baseDenom: 'uusdc',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
26
37
|
];
|
|
27
|
-
harden(
|
|
38
|
+
harden(defaultAssetInfo);
|
|
28
39
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
40
|
+
const agoricAssetInfo = defaultAssetInfo.filter(
|
|
41
|
+
([_d, i]) => i.chainName === 'agoric',
|
|
42
|
+
);
|
|
32
43
|
|
|
33
44
|
/**
|
|
34
45
|
* @type {Record<string, Pick<FastUSDCConfig, 'oracles' | 'feedPolicy' | 'chainInfo' | 'assetInfo' >>}
|
|
35
46
|
*
|
|
47
|
+
* TODO: determine OCW operator addresses
|
|
36
48
|
* meanwhile, use price oracle addresses (from updatePriceFeeds.js).
|
|
37
49
|
*/
|
|
38
50
|
export const configurations = {
|
|
@@ -50,8 +62,16 @@ export const configurations = {
|
|
|
50
62
|
feedPolicy: {
|
|
51
63
|
nobleAgoricChannelId: 'channel-does-not-exist',
|
|
52
64
|
nobleDomainId: 4,
|
|
53
|
-
chainPolicies:
|
|
54
|
-
|
|
65
|
+
chainPolicies: {
|
|
66
|
+
Arbitrum: {
|
|
67
|
+
attenuatedCttpBridgeAddress:
|
|
68
|
+
'0xe298b93ffB5eA1FB628e0C0D55A43aeaC268e347',
|
|
69
|
+
cctpTokenMessengerAddress:
|
|
70
|
+
'0x19330d10D9Cc8751218eaf51E8885D058642E08A',
|
|
71
|
+
chainId: 42161,
|
|
72
|
+
confirmations: 2,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
55
75
|
},
|
|
56
76
|
chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
|
|
57
77
|
withChainCapabilities({
|
|
@@ -60,25 +80,32 @@ export const configurations = {
|
|
|
60
80
|
noble: fetchedChainInfo.noble,
|
|
61
81
|
})
|
|
62
82
|
),
|
|
63
|
-
assetInfo:
|
|
83
|
+
assetInfo: agoricAssetInfo,
|
|
64
84
|
},
|
|
65
85
|
MAINNET: {
|
|
66
|
-
// per JVC 12 Feb 2025
|
|
67
86
|
oracles: {
|
|
68
|
-
'01node': '
|
|
69
|
-
|
|
70
|
-
|
|
87
|
+
'01node': 'agoric19uscwxdac6cf6z7d5e26e0jm0lgwstc47cpll8',
|
|
88
|
+
'Simply Staking': 'agoric1krunjcqfrf7la48zrvdfeeqtls5r00ep68mzkr',
|
|
89
|
+
P2P: 'agoric1n4fcxsnkxe4gj6e24naec99hzmc4pjfdccy5nj',
|
|
71
90
|
},
|
|
72
91
|
feedPolicy: {
|
|
73
92
|
nobleAgoricChannelId: 'channel-21',
|
|
74
93
|
nobleDomainId: 4,
|
|
75
|
-
chainPolicies:
|
|
76
|
-
|
|
94
|
+
chainPolicies: {
|
|
95
|
+
Arbitrum: {
|
|
96
|
+
attenuatedCttpBridgeAddress:
|
|
97
|
+
'0xe298b93ffB5eA1FB628e0C0D55A43aeaC268e347',
|
|
98
|
+
cctpTokenMessengerAddress:
|
|
99
|
+
'0x19330d10D9Cc8751218eaf51E8885D058642E08A',
|
|
100
|
+
chainId: 42161,
|
|
101
|
+
confirmations: 2,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
77
104
|
},
|
|
78
105
|
chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
|
|
79
106
|
withChainCapabilities(fetchedChainInfo)
|
|
80
107
|
),
|
|
81
|
-
assetInfo:
|
|
108
|
+
assetInfo: defaultAssetInfo,
|
|
82
109
|
},
|
|
83
110
|
DEVNET: {
|
|
84
111
|
oracles: {
|
|
@@ -91,13 +118,19 @@ export const configurations = {
|
|
|
91
118
|
feedPolicy: {
|
|
92
119
|
nobleAgoricChannelId: 'TODO',
|
|
93
120
|
nobleDomainId: 4,
|
|
94
|
-
chainPolicies:
|
|
95
|
-
|
|
121
|
+
chainPolicies: {
|
|
122
|
+
Arbitrum: {
|
|
123
|
+
attenuatedCttpBridgeAddress: '0xTODO',
|
|
124
|
+
cctpTokenMessengerAddress: '0xTODO',
|
|
125
|
+
chainId: 421614,
|
|
126
|
+
confirmations: 2,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
96
129
|
},
|
|
97
130
|
chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
|
|
98
131
|
withChainCapabilities(fetchedChainInfo) // TODO: use devnet values
|
|
99
132
|
),
|
|
100
|
-
assetInfo:
|
|
133
|
+
assetInfo: defaultAssetInfo, // TODO: use emerynet values
|
|
101
134
|
},
|
|
102
135
|
EMERYNET: {
|
|
103
136
|
oracles: {
|
|
@@ -107,13 +140,19 @@ export const configurations = {
|
|
|
107
140
|
feedPolicy: {
|
|
108
141
|
nobleAgoricChannelId: 'TODO',
|
|
109
142
|
nobleDomainId: 4,
|
|
110
|
-
chainPolicies:
|
|
111
|
-
|
|
143
|
+
chainPolicies: {
|
|
144
|
+
Arbitrum: {
|
|
145
|
+
attenuatedCttpBridgeAddress: '0xTODO',
|
|
146
|
+
cctpTokenMessengerAddress: '0xTODO',
|
|
147
|
+
chainId: 421614,
|
|
148
|
+
confirmations: 2,
|
|
149
|
+
},
|
|
150
|
+
},
|
|
112
151
|
},
|
|
113
152
|
chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
|
|
114
153
|
withChainCapabilities(fetchedChainInfo) // TODO: use emerynet values
|
|
115
154
|
),
|
|
116
|
-
assetInfo:
|
|
155
|
+
assetInfo: defaultAssetInfo, // TODO: use emerynet values
|
|
117
156
|
},
|
|
118
157
|
};
|
|
119
158
|
harden(configurations);
|
package/src/utils/fees.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Fail } from '@endo/errors';
|
|
|
4
4
|
import { mustMatch } from '@endo/patterns';
|
|
5
5
|
import { FeeConfigShape } from '../type-guards.js';
|
|
6
6
|
|
|
7
|
-
const { add, isGTE, subtract } = AmountMath;
|
|
7
|
+
const { add, isGTE, min, subtract } = AmountMath;
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @import {Amount} from '@agoric/ertp';
|
|
@@ -15,7 +15,7 @@ const { add, isGTE, subtract } = AmountMath;
|
|
|
15
15
|
/** @param {FeeConfig} feeConfig */
|
|
16
16
|
export const makeFeeTools = feeConfig => {
|
|
17
17
|
mustMatch(feeConfig, FeeConfigShape, 'Must provide feeConfig');
|
|
18
|
-
const { flat, variableRate } = feeConfig;
|
|
18
|
+
const { flat, variableRate, maxVariable } = feeConfig;
|
|
19
19
|
const feeTools = harden({
|
|
20
20
|
/**
|
|
21
21
|
* Calculate the net amount to advance after withholding fees.
|
|
@@ -34,7 +34,8 @@ export const makeFeeTools = feeConfig => {
|
|
|
34
34
|
* @throws {Error} if requested does not exceed fees
|
|
35
35
|
*/
|
|
36
36
|
calculateAdvanceFee(requested) {
|
|
37
|
-
const
|
|
37
|
+
const variable = min(multiplyBy(requested, variableRate), maxVariable);
|
|
38
|
+
const fee = add(variable, flat);
|
|
38
39
|
!isGTE(fee, requested) || Fail`Request must exceed fees.`;
|
|
39
40
|
return fee;
|
|
40
41
|
},
|