@agoric/fast-usdc 0.1.1-other-dev-3eb1a1d.0 → 0.1.1-other-dev-fbe72e7.0.fbe72e7

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.
@@ -0,0 +1,98 @@
1
+ // @ts-check
2
+ import { assertAllDefined } from '@agoric/internal';
3
+
4
+ /**
5
+ * @import {USDCProposalShapes} from './pool-share-math.js';
6
+ */
7
+
8
+ /**
9
+ * @param {Pick<
10
+ * import('@agoric/vats/tools/board-utils.js').AgoricNamesRemotes,
11
+ * 'brand'
12
+ * >} agoricNames
13
+ * @param {object} opts
14
+ * @param {string} opts.offerId
15
+ * @param {bigint} opts.fastLPAmount
16
+ * @param {bigint} opts.usdcAmount
17
+ * @returns {import('@agoric/smart-wallet/src/offers.js').OfferSpec & {proposal: USDCProposalShapes['deposit']}}
18
+ */
19
+ const makeDepositOffer = ({ brand }, { offerId, fastLPAmount, usdcAmount }) => {
20
+ assertAllDefined({ offerId, fastLPAmount, usdcAmount });
21
+
22
+ return {
23
+ id: offerId,
24
+ invitationSpec: {
25
+ source: 'agoricContract',
26
+ instancePath: ['fastUsdc'],
27
+ callPipe: [['makeDepositInvitation']],
28
+ },
29
+ /** @type {USDCProposalShapes['deposit']} */
30
+ // @ts-expect-error https://github.com/Agoric/agoric-sdk/issues/10491
31
+ proposal: {
32
+ give: {
33
+ USDC: {
34
+ brand: brand.USDC,
35
+ value: usdcAmount,
36
+ },
37
+ },
38
+ want: {
39
+ PoolShare: {
40
+ brand: brand.FastLP,
41
+ value: fastLPAmount,
42
+ },
43
+ },
44
+ },
45
+ };
46
+ };
47
+
48
+ /**
49
+ * @param {Pick<
50
+ * import('@agoric/vats/tools/board-utils.js').AgoricNamesRemotes,
51
+ * 'brand'
52
+ * >} agoricNames
53
+ * @param {object} opts
54
+ * @param {string} opts.offerId
55
+ * @param {bigint} opts.fastLPAmount
56
+ * @param {bigint} opts.usdcAmount
57
+ * @returns {import('@agoric/smart-wallet/src/offers.js').OfferSpec}
58
+ */
59
+ const makeWithdrawOffer = (
60
+ { brand },
61
+ { offerId, fastLPAmount, usdcAmount },
62
+ ) => ({
63
+ id: offerId,
64
+ invitationSpec: {
65
+ source: 'agoricContract',
66
+ instancePath: ['fastUsdc'],
67
+ callPipe: [['makeWithdrawInvitation']],
68
+ },
69
+ proposal: {
70
+ give: {
71
+ PoolShare: {
72
+ // @ts-expect-error https://github.com/Agoric/agoric-sdk/issues/10491
73
+ brand: brand.FastLP,
74
+ value: fastLPAmount,
75
+ },
76
+ },
77
+ want: {
78
+ USDC: {
79
+ // @ts-expect-error https://github.com/Agoric/agoric-sdk/issues/10491
80
+ brand: brand.USDC,
81
+ value: usdcAmount,
82
+ },
83
+ },
84
+ },
85
+ });
86
+
87
+ /**
88
+ * @satisfies {Record<
89
+ * string,
90
+ * Record<string, import('@agoric/smart-wallet/src/types.js').OfferMaker>
91
+ * >}
92
+ */
93
+ export const Offers = {
94
+ fastUsdc: {
95
+ Deposit: makeDepositOffer,
96
+ Withdraw: makeWithdrawOffer,
97
+ },
98
+ };
package/src/constants.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Status values for FastUSDC.
2
+ * Status values for FastUSDC. Includes states for advancing and settling.
3
3
  *
4
4
  * @enum {(typeof TxStatus)[keyof typeof TxStatus]}
5
5
  */
@@ -7,21 +7,44 @@ export const TxStatus = /** @type {const} */ ({
7
7
  /** tx was observed but not advanced */
8
8
  Observed: 'OBSERVED',
9
9
  /** IBC transfer is initiated */
10
+ Advancing: 'ADVANCING',
11
+ /** IBC transfer is complete */
10
12
  Advanced: 'ADVANCED',
11
- /** settlement for matching advance received and funds dispersed */
12
- Settled: 'SETTLED',
13
+ /** IBC transfer failed (timed out) */
14
+ AdvanceFailed: 'ADVANCE_FAILED',
15
+ /** Advance skipped and waiting for forward */
16
+ AdvanceSkipped: 'ADVANCE_SKIPPED',
17
+ /** settlement for matching advance received and funds disbursed */
18
+ Disbursed: 'DISBURSED',
19
+ /** No route, forward not attempted */
20
+ ForwardedSkipped: 'FORWARD_SKIPPED',
21
+ /** fallback: do not collect fees */
22
+ Forwarded: 'FORWARDED',
23
+ /** failed to forward to EUD */
24
+ ForwardFailed: 'FORWARD_FAILED',
13
25
  });
14
26
  harden(TxStatus);
15
27
 
28
+ // According to the state diagram
29
+ export const TerminalTxStatus = {
30
+ [TxStatus.Forwarded]: true,
31
+ [TxStatus.ForwardFailed]: true,
32
+ [TxStatus.Disbursed]: true,
33
+ };
34
+
16
35
  /**
17
- * Status values for the StatusManager.
36
+ * Status values for the StatusManager while an advance is being processed.
18
37
  *
19
38
  * @enum {(typeof PendingTxStatus)[keyof typeof PendingTxStatus]}
20
39
  */
21
40
  export const PendingTxStatus = /** @type {const} */ ({
22
- /** tx was observed but not advanced */
23
- Observed: 'OBSERVED',
24
41
  /** IBC transfer is initiated */
42
+ Advancing: 'ADVANCING',
43
+ /** IBC transfer failed (timed out) */
44
+ AdvanceFailed: 'ADVANCE_FAILED',
45
+ /** IBC transfer is complete */
25
46
  Advanced: 'ADVANCED',
47
+ /** Advance skipped and waiting for forward */
48
+ AdvanceSkipped: 'ADVANCE_SKIPPED',
26
49
  });
27
50
  harden(PendingTxStatus);
package/src/main.js ADDED
@@ -0,0 +1 @@
1
+ export * from './types.js';
@@ -0,0 +1,29 @@
1
+ import { M } from '@endo/patterns';
2
+ import { CctpTxEvidenceShape, RiskAssessmentShape } from './type-guards.js';
3
+
4
+ /**
5
+ * @import {Zone} from '@agoric/zone';
6
+ * @import {CctpTxEvidence, RiskAssessment} from '@agoric/fast-usdc/src/types.js';
7
+ */
8
+
9
+ /** Name in the invitation purse (keyed also by this contract instance) */
10
+ export const INVITATION_MAKERS_DESC = 'oracle operator invitation';
11
+
12
+ export const OperatorKitI = {
13
+ admin: M.interface('Admin', {
14
+ disable: M.call().returns(),
15
+ }),
16
+
17
+ invitationMakers: M.interface('InvitationMakers', {
18
+ SubmitEvidence: M.call(CctpTxEvidenceShape)
19
+ .optional(RiskAssessmentShape)
20
+ .returns(M.promise()),
21
+ }),
22
+
23
+ operator: M.interface('Operator', {
24
+ submitEvidence: M.call(CctpTxEvidenceShape)
25
+ .optional(RiskAssessmentShape)
26
+ .returns(),
27
+ getStatus: M.call().returns(M.record()),
28
+ }),
29
+ };
@@ -4,21 +4,23 @@ import {
4
4
  makeRatio,
5
5
  makeRatioFromAmounts,
6
6
  multiplyBy,
7
- } from '@agoric/zoe/src/contractSupport/ratio.js';
7
+ } from '@agoric/ertp/src/ratio.js';
8
8
  import { Fail, q } from '@endo/errors';
9
9
 
10
- const { getValue, add, isEmpty, isEqual, isGTE, subtract } = AmountMath;
10
+ const { keys } = Object;
11
+ const { add, isEmpty, isEqual, isGTE, make, makeEmpty, subtract } = AmountMath;
11
12
 
12
13
  /**
13
- * @import {PoolStats} from './types';
14
- * @import {RepayAmountKWR} from './exos/liquidity-pool';
14
+ * @import {Amount, Brand, DepositFacet, NatValue, Payment, Ratio} from '@agoric/ertp';
15
+ * @import {Allocation} from '@agoric/zoe';
16
+ * @import {PoolStats} from './types.js';
17
+ * @import {RepayAmountKWR} from './utils/fees.js';
15
18
  */
16
19
 
17
20
  /**
18
21
  * Invariant: shareWorth is the pool balance divided by shares outstanding.
19
22
  *
20
- * Use `makeParity(make(USDC, epsilon), PoolShares)` for an initial
21
- * value, for some negligible `epsilon` such as 1n.
23
+ * Use `makeParity(USDC, PoolShares)` for an initial value.
22
24
  *
23
25
  * @typedef {Ratio} ShareWorth
24
26
  */
@@ -26,23 +28,26 @@ const { getValue, add, isEmpty, isEqual, isGTE, subtract } = AmountMath;
26
28
  /**
27
29
  * Make a 1-to-1 ratio between amounts of 2 brands.
28
30
  *
29
- * @param {Amount<'nat'>} numerator
31
+ * @param {Brand<'nat'>} numeratorBrand
30
32
  * @param {Brand<'nat'>} denominatorBrand
31
33
  */
32
- export const makeParity = (numerator, denominatorBrand) => {
33
- const value = getValue(numerator.brand, numerator);
34
- return makeRatio(value, numerator.brand, value, denominatorBrand);
34
+ export const makeParity = (numeratorBrand, denominatorBrand) => {
35
+ const dust = 1n;
36
+ return makeRatio(dust, numeratorBrand, dust, denominatorBrand);
35
37
  };
36
38
 
37
39
  /**
38
40
  * @typedef {{
39
41
  * deposit: {
40
42
  * give: { USDC: Amount<'nat'> },
41
- * want?: { PoolShare: Amount<'nat'> }
43
+ * want: { PoolShare: Amount<'nat'> }
42
44
  * },
43
45
  * withdraw: {
44
46
  * give: { PoolShare: Amount<'nat'> }
45
47
  * want: { USDC: Amount<'nat'> },
48
+ * },
49
+ * withdrawFees: {
50
+ * want: { USDC: Amount<'nat'> }
46
51
  * }
47
52
  * }} USDCProposalShapes
48
53
  */
@@ -91,14 +96,54 @@ export const depositCalc = (shareWorth, { give, want }) => {
91
96
  });
92
97
  };
93
98
 
99
+ /**
100
+ * Verifies that the total pool balance (unencumbered + encumbered) matches the
101
+ * shareWorth numerator. The total pool balance consists of:
102
+ * 1. unencumbered balance - USDC available in the pool for borrowing
103
+ * 2. encumbered balance - USDC currently lent out
104
+ *
105
+ * A negligible `dust` amount is used to initialize shareWorth with a non-zero
106
+ * denominator. It must remain in the pool at all times.
107
+ *
108
+ * @param {Allocation} poolAlloc
109
+ * @param {ShareWorth} shareWorth
110
+ * @param {Amount<'nat'>} encumberedBalance
111
+ */
112
+ export const checkPoolBalance = (poolAlloc, shareWorth, encumberedBalance) => {
113
+ const { brand: usdcBrand } = encumberedBalance;
114
+ const unencumberedBalance = poolAlloc.USDC || makeEmpty(usdcBrand);
115
+ const kwds = keys(poolAlloc);
116
+ kwds.length === 0 ||
117
+ (kwds.length === 1 && kwds[0] === 'USDC') ||
118
+ Fail`unexpected pool allocations: ${poolAlloc}`;
119
+ const dust = make(usdcBrand, 1n);
120
+ const grossBalance = add(add(unencumberedBalance, dust), encumberedBalance);
121
+ isEqual(grossBalance, shareWorth.numerator) ||
122
+ Fail`🚨 pool balance ${q(unencumberedBalance)} and encumbered balance ${q(encumberedBalance)} inconsistent with shareWorth ${q(shareWorth)}`;
123
+ return harden({ unencumberedBalance, grossBalance });
124
+ };
125
+
94
126
  /**
95
127
  * Compute payout from a withdraw proposal, along with updated shareWorth
96
128
  *
97
129
  * @param {ShareWorth} shareWorth
98
130
  * @param {USDCProposalShapes['withdraw']} proposal
131
+ * @param {Allocation} poolAlloc
132
+ * @param {Amount<'nat'>} [encumberedBalance]
99
133
  * @returns {{ shareWorth: ShareWorth, payouts: { USDC: Amount<'nat'> }}}
100
134
  */
101
- export const withdrawCalc = (shareWorth, { give, want }) => {
135
+ export const withdrawCalc = (
136
+ shareWorth,
137
+ { give, want },
138
+ poolAlloc,
139
+ encumberedBalance = makeEmpty(shareWorth.numerator.brand),
140
+ ) => {
141
+ const { unencumberedBalance } = checkPoolBalance(
142
+ poolAlloc,
143
+ shareWorth,
144
+ encumberedBalance,
145
+ );
146
+
102
147
  assert(!isEmpty(give.PoolShare));
103
148
  assert(!isEmpty(want.USDC));
104
149
 
@@ -108,6 +153,8 @@ export const withdrawCalc = (shareWorth, { give, want }) => {
108
153
  const { denominator: sharesOutstanding, numerator: poolBalance } = shareWorth;
109
154
  !isGTE(want.USDC, poolBalance) ||
110
155
  Fail`cannot withdraw ${q(want.USDC)}; only ${q(poolBalance)} in pool`;
156
+ isGTE(unencumberedBalance, want.USDC) ||
157
+ Fail`cannot withdraw ${q(want.USDC)}; ${q(encumberedBalance)} is in use; stand by for pool to return to ${q(poolBalance)}`;
111
158
  const balancePost = subtract(poolBalance, payout);
112
159
  // giving more shares than are outstanding is impossible,
113
160
  // so it's not worth a custom diagnostic. subtract will fail
@@ -155,35 +202,26 @@ export const borrowCalc = (
155
202
 
156
203
  /**
157
204
  * @param {ShareWorth} shareWorth
158
- * @param {Allocation} fromSeatAllocation
159
- * @param {RepayAmountKWR} amounts
205
+ * @param {RepayAmountKWR} split
160
206
  * @param {Amount<'nat'>} encumberedBalance aka 'outstanding borrows'
161
207
  * @param {PoolStats} poolStats
162
- * @throws {Error} if allocations do not match amounts or Principal exceeds encumberedBalance
208
+ * @throws {Error} if Principal exceeds encumberedBalance
163
209
  */
164
- export const repayCalc = (
165
- shareWorth,
166
- fromSeatAllocation,
167
- amounts,
168
- encumberedBalance,
169
- poolStats,
170
- ) => {
171
- (isEqual(fromSeatAllocation.Principal, amounts.Principal) &&
172
- isEqual(fromSeatAllocation.PoolFee, amounts.PoolFee) &&
173
- isEqual(fromSeatAllocation.ContractFee, amounts.ContractFee)) ||
174
- Fail`Cannot repay. From seat allocation ${q(fromSeatAllocation)} does not equal amounts ${q(amounts)}.`;
175
-
176
- isGTE(encumberedBalance, amounts.Principal) ||
177
- Fail`Cannot repay. Principal ${q(amounts.Principal)} exceeds encumberedBalance ${q(encumberedBalance)}.`;
210
+ export const repayCalc = (shareWorth, split, encumberedBalance, poolStats) => {
211
+ isGTE(encumberedBalance, split.Principal) ||
212
+ Fail`Cannot repay. Principal ${q(split.Principal)} exceeds encumberedBalance ${q(encumberedBalance)}.`;
178
213
 
179
214
  return harden({
180
- shareWorth: withFees(shareWorth, amounts.PoolFee),
181
- encumberedBalance: subtract(encumberedBalance, amounts.Principal),
215
+ shareWorth: withFees(shareWorth, split.PoolFee),
216
+ encumberedBalance: subtract(encumberedBalance, split.Principal),
182
217
  poolStats: {
183
218
  ...poolStats,
184
- totalRepays: add(poolStats.totalRepays, amounts.Principal),
185
- totalPoolFees: add(poolStats.totalPoolFees, amounts.PoolFee),
186
- totalContractFees: add(poolStats.totalContractFees, amounts.ContractFee),
219
+ totalRepays: add(poolStats.totalRepays, split.Principal),
220
+ totalPoolFees: add(poolStats.totalPoolFees, split.PoolFee),
221
+ totalContractFees: add(
222
+ add(poolStats.totalContractFees, split.ContractFee),
223
+ split.RelayFee,
224
+ ),
187
225
  },
188
226
  });
189
227
  };
@@ -1,16 +1,21 @@
1
1
  import { AmountShape, BrandShape, RatioShape } from '@agoric/ertp';
2
2
  import { M } from '@endo/patterns';
3
+ import {
4
+ DenomDetailShape,
5
+ DenomShape,
6
+ } from '@agoric/orchestration/src/typeGuards.js';
3
7
  import { PendingTxStatus } from './constants.js';
4
8
 
5
9
  /**
10
+ * @import {Amount, Brand, NatValue, Payment} from '@agoric/ertp';
6
11
  * @import {TypedPattern} from '@agoric/internal';
7
- * @import {FastUsdcTerms} from './fast-usdc.contract.js';
12
+ * @import {CosmosChainInfo, Denom, DenomDetail, OrchestrationAccount, IBCConnectionInfo, CaipChainId} from '@agoric/orchestration';
8
13
  * @import {USDCProposalShapes} from './pool-share-math.js';
9
- * @import {CctpTxEvidence, FeeConfig, PendingTx, PoolMetrics, ChainPolicy, FeedPolicy} from './types.js';
14
+ * @import {CctpTxEvidence, FastUSDCConfig, FastUsdcTerms, FeeConfig, PendingTx, PoolMetrics, ChainPolicy, FeedPolicy, AddressHook, EvmAddress, EvmHash, RiskAssessment, EvidenceWithRisk} from './types.js';
10
15
  */
11
16
 
12
17
  /**
13
- * @param {Brand} brand must be a 'nat' brand, not checked
18
+ * @param {Brand<'nat'>} brand must be a 'nat' brand, not checked
14
19
  * @param {NatValue} [min]
15
20
  */
16
21
  export const makeNatAmountShape = (brand, min) =>
@@ -19,16 +24,20 @@ export const makeNatAmountShape = (brand, min) =>
19
24
  /** @param {Record<'PoolShares' | 'USDC', Brand<'nat'>>} brands */
20
25
  export const makeProposalShapes = ({ PoolShares, USDC }) => {
21
26
  /** @type {TypedPattern<USDCProposalShapes['deposit']>} */
22
- const deposit = M.splitRecord(
23
- { give: { USDC: makeNatAmountShape(USDC, 1n) } },
24
- { want: { PoolShare: makeNatAmountShape(PoolShares) } },
25
- );
27
+ const deposit = M.splitRecord({
28
+ give: { USDC: makeNatAmountShape(USDC, 1n) },
29
+ want: { PoolShare: makeNatAmountShape(PoolShares) },
30
+ });
26
31
  /** @type {TypedPattern<USDCProposalShapes['withdraw']>} */
27
32
  const withdraw = M.splitRecord({
28
33
  give: { PoolShare: makeNatAmountShape(PoolShares, 1n) },
29
34
  want: { USDC: makeNatAmountShape(USDC, 1n) },
30
35
  });
31
- return harden({ deposit, withdraw });
36
+ /** @type {TypedPattern<USDCProposalShapes['withdrawFees']>} */
37
+ const withdrawFees = M.splitRecord({
38
+ want: { USDC: makeNatAmountShape(USDC, 1n) },
39
+ });
40
+ return harden({ deposit, withdraw, withdrawFees });
32
41
  };
33
42
 
34
43
  /** @type {TypedPattern<FastUsdcTerms>} */
@@ -36,12 +45,28 @@ export const FastUSDCTermsShape = harden({
36
45
  usdcDenom: M.string(),
37
46
  });
38
47
 
39
- /** @type {TypedPattern<string>} */
48
+ /** @type {TypedPattern<EvmAddress>} */
49
+ export const EvmAddressShape = M.string({
50
+ // 0x + 40 hex digits
51
+ stringLengthLimit: 42,
52
+ });
53
+ harden(EvmAddressShape);
54
+
55
+ /** @type {TypedPattern<EvmHash>} */
40
56
  export const EvmHashShape = M.string({
41
57
  stringLengthLimit: 66,
42
58
  });
43
59
  harden(EvmHashShape);
44
60
 
61
+ /** @type {TypedPattern<RiskAssessment>} */
62
+ export const RiskAssessmentShape = M.splitRecord(
63
+ {},
64
+ {
65
+ risksIdentified: M.arrayOf(M.string()),
66
+ },
67
+ );
68
+ harden(RiskAssessmentShape);
69
+
45
70
  /** @type {TypedPattern<CctpTxEvidence>} */
46
71
  export const CctpTxEvidenceShape = {
47
72
  aux: {
@@ -49,17 +74,25 @@ export const CctpTxEvidenceShape = {
49
74
  recipientAddress: M.string(),
50
75
  },
51
76
  blockHash: EvmHashShape,
52
- blockNumber: M.bigint(),
53
- blockTimestamp: M.bigint(),
77
+ blockNumber: M.nat(),
78
+ blockTimestamp: M.nat(),
54
79
  chainId: M.number(),
55
80
  tx: {
56
- amount: M.bigint(),
81
+ amount: M.nat(),
57
82
  forwardingAddress: M.string(),
83
+ sender: EvmAddressShape,
58
84
  },
59
85
  txHash: EvmHashShape,
60
86
  };
61
87
  harden(CctpTxEvidenceShape);
62
88
 
89
+ /** @type {TypedPattern<EvidenceWithRisk>} */
90
+ export const EvidenceWithRiskShape = {
91
+ evidence: CctpTxEvidenceShape,
92
+ risk: RiskAssessmentShape,
93
+ };
94
+ harden(EvidenceWithRiskShape);
95
+
63
96
  /** @type {TypedPattern<PendingTx>} */
64
97
  // @ts-expect-error TypedPattern not recognized as record
65
98
  export const PendingTxShape = {
@@ -68,20 +101,42 @@ export const PendingTxShape = {
68
101
  };
69
102
  harden(PendingTxShape);
70
103
 
71
- export const EudParamShape = {
72
- EUD: M.string(),
104
+ /** @type {TypedPattern<AddressHook>} */
105
+ export const AddressHookShape = {
106
+ baseAddress: M.string(),
107
+ query: { EUD: M.string() },
73
108
  };
74
- harden(EudParamShape);
109
+ harden(AddressHookShape);
75
110
 
76
111
  const NatAmountShape = { brand: BrandShape, value: M.nat() };
112
+
113
+ /** @type {TypedPattern<FeeConfig['destinationOverrides']>} */
114
+ export const DestinationOverridesShape = M.recordOf(
115
+ M.string(),
116
+ M.splitRecord(
117
+ {},
118
+ {
119
+ flat: NatAmountShape,
120
+ variableRate: RatioShape,
121
+ contractRate: RatioShape,
122
+ relay: NatAmountShape,
123
+ },
124
+ ),
125
+ );
126
+
77
127
  /** @type {TypedPattern<FeeConfig>} */
78
- export const FeeConfigShape = {
79
- flat: NatAmountShape,
80
- variableRate: RatioShape,
81
- maxVariable: NatAmountShape,
82
- contractRate: RatioShape,
83
- };
84
- harden(FeeConfigShape);
128
+ export const FeeConfigShape = M.splitRecord(
129
+ {
130
+ flat: NatAmountShape,
131
+ variableRate: RatioShape,
132
+ contractRate: RatioShape,
133
+ },
134
+ {
135
+ relay: NatAmountShape,
136
+ destinationOverrides: DestinationOverridesShape,
137
+ },
138
+ {},
139
+ );
85
140
 
86
141
  /** @type {TypedPattern<PoolMetrics>} */
87
142
  export const PoolMetricsShape = {
@@ -95,29 +150,62 @@ export const PoolMetricsShape = {
95
150
  harden(PoolMetricsShape);
96
151
 
97
152
  /** @type {TypedPattern<ChainPolicy>} */
98
- export const ChainPoliciesShape = M.splitRecord(
99
- {
100
- nobleContractAddress: EvmHashShape,
101
- cctpTokenMessengerAddress: EvmHashShape,
102
- confirmations: M.number(),
103
- chainId: M.number(),
153
+ export const ChainPolicyShape = {
154
+ attenuatedCttpBridgeAddresses: M.splitArray(
155
+ [EvmHashShape],
156
+ undefined,
157
+ M.arrayOf(EvmHashShape),
158
+ ),
159
+ cctpTokenMessengerAddress: EvmHashShape,
160
+ confirmations: M.number(),
161
+ chainId: M.number(),
162
+ rateLimits: {
163
+ tx: M.bigint(),
164
+ blockWindow: M.bigint(),
165
+ blockWindowSize: M.number(),
104
166
  },
105
- { chainType: M.number() },
106
- );
107
- harden(ChainPoliciesShape);
167
+ };
168
+ harden(ChainPolicyShape);
108
169
 
109
170
  /**
110
171
  * @type {TypedPattern<FeedPolicy>}
111
172
  *
112
- * Should be JSON serializable so CLI can specify policy. E.g. no bigint,
113
- * undefined, remotable, etc.
173
+ * must be CopyData; no Brands or other Remotables
114
174
  */
115
175
  export const FeedPolicyShape = M.splitRecord(
116
176
  {
117
177
  nobleDomainId: M.number(),
118
178
  nobleAgoricChannelId: M.string(),
119
- chainPolicies: M.recordOf(M.string(), ChainPoliciesShape),
179
+ chainPolicies: M.recordOf(M.string(), ChainPolicyShape),
120
180
  },
121
181
  { eventFilter: M.string() },
122
182
  );
123
183
  harden(FeedPolicyShape);
184
+
185
+ /**
186
+ * The version of CosmosChainInfoShape that matches the `valueShape` used in FUSDC's ChainHub's `chainInfos` mapStore.
187
+ * @type {TypedPattern<CosmosChainInfo>}
188
+ */
189
+ export const CosmosChainInfoShapeV1 = M.splitRecord(
190
+ {
191
+ chainId: M.string(),
192
+ },
193
+ {
194
+ bech32Prefix: M.string(),
195
+ connections: M.record(),
196
+ stakingTokens: M.arrayOf({ denom: M.string() }),
197
+ // UNTIL https://github.com/Agoric/agoric-sdk/issues/9326
198
+ icqEnabled: M.boolean(),
199
+ pfmEnabled: M.boolean(),
200
+ },
201
+ );
202
+
203
+ /** @type {TypedPattern<FastUSDCConfig>} */
204
+ export const FastUSDCConfigShape = M.splitRecord({
205
+ terms: FastUSDCTermsShape,
206
+ oracles: M.recordOf(M.string(), M.string()),
207
+ feeConfig: FeeConfigShape,
208
+ feedPolicy: FeedPolicyShape,
209
+ chainInfo: M.recordOf(M.string(), CosmosChainInfoShapeV1),
210
+ assetInfo: M.arrayOf([DenomShape, DenomDetailShape]),
211
+ });