@agoric/fast-usdc 0.1.1-other-dev-3eb1a1d.0 → 0.1.1-upgrade-19-dev-c605745.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 (40) hide show
  1. package/README.md +43 -0
  2. package/package.json +29 -25
  3. package/src/add-operators.core.js +63 -0
  4. package/src/cli/bridge-action.js +40 -0
  5. package/src/cli/cli.js +46 -153
  6. package/src/cli/config-commands.js +108 -0
  7. package/src/cli/config.js +15 -9
  8. package/src/cli/lp-commands.js +160 -0
  9. package/src/cli/operator-commands.js +146 -0
  10. package/src/cli/transfer.js +63 -13
  11. package/src/{util → cli/util}/agoric.js +1 -1
  12. package/src/cli/util/bank.js +12 -0
  13. package/src/{util → cli/util}/cctp.js +1 -1
  14. package/src/{util → cli/util}/file.js +1 -1
  15. package/src/clientSupport.js +98 -0
  16. package/src/constants.js +25 -2
  17. package/src/distribute-fees.core.js +93 -0
  18. package/src/exos/advancer.js +220 -106
  19. package/src/exos/liquidity-pool.js +130 -81
  20. package/src/exos/operator-kit.js +16 -12
  21. package/src/exos/settler.js +360 -64
  22. package/src/exos/status-manager.js +316 -65
  23. package/src/exos/transaction-feed.js +121 -42
  24. package/src/fast-usdc-policy.core.js +65 -0
  25. package/src/fast-usdc.contract.js +165 -84
  26. package/src/fast-usdc.flows.js +10 -0
  27. package/src/main.js +1 -0
  28. package/src/pool-share-math.js +55 -9
  29. package/src/{fast-usdc.start.js → start-fast-usdc.core.js} +48 -86
  30. package/src/type-guards.js +75 -24
  31. package/src/types.ts +89 -14
  32. package/src/utils/chain-policies.js +140 -0
  33. package/src/utils/core-eval.js +73 -0
  34. package/src/utils/deploy-config.js +127 -0
  35. package/src/utils/fees.js +3 -4
  36. package/tools/cli-tools.ts +9 -0
  37. package/tools/mock-io.ts +14 -0
  38. package/src/exos/README.md +0 -26
  39. package/src/utils/address.js +0 -71
  40. /package/src/{util → cli/util}/noble.js +0 -0
@@ -0,0 +1,140 @@
1
+ /** @import {ChainPolicy} from '../types.js'; */
2
+
3
+ /** @satisfies {Record<'MAINNET'| 'TESTNET', Record<string, ChainPolicy>>} */
4
+ export const ChainPolicies = /** @type {const} */ ({
5
+ MAINNET: {
6
+ Arbitrum: {
7
+ attenuatedCttpBridgeAddresses: [
8
+ '0xe298b93ffB5eA1FB628e0C0D55A43aeaC268e347',
9
+ ],
10
+ cctpTokenMessengerAddress: '0x19330d10D9Cc8751218eaf51E8885D058642E08A',
11
+ chainId: 42161,
12
+ // TODO confirm confirmations and rateLimits
13
+ confirmations: 2,
14
+ rateLimits: {
15
+ blockWindow: 20_000_000_000n,
16
+ blockWindowSize: 10,
17
+ tx: 10_000_000_000n,
18
+ },
19
+ },
20
+ Base: {
21
+ attenuatedCttpBridgeAddresses: [
22
+ '0xB6615B2662b35fc3533F8479002e62D0523341De',
23
+ ],
24
+ cctpTokenMessengerAddress: '0x1682Ae6375C4E4A97e4B583BC394c861A46D8962',
25
+ chainId: 8453,
26
+ // TODO confirm confirmations and rateLimits
27
+ confirmations: 2,
28
+ rateLimits: {
29
+ blockWindow: 20_000_000_000n,
30
+ blockWindowSize: 10,
31
+ tx: 10_000_000_000n,
32
+ },
33
+ },
34
+ Ethereum: {
35
+ attenuatedCttpBridgeAddresses: [
36
+ '0xBC8552339dA68EB65C8b88B414B5854E0E366cFc',
37
+ ],
38
+ cctpTokenMessengerAddress: '0xBd3fa81B58Ba92a82136038B25aDec7066af3155',
39
+ chainId: 1,
40
+ // TODO confirm confirmations and rateLimits
41
+ confirmations: 2,
42
+ rateLimits: {
43
+ blockWindow: 20_000_000_000n,
44
+ blockWindowSize: 10,
45
+ tx: 10_000_000_000n,
46
+ },
47
+ },
48
+ Optimism: {
49
+ attenuatedCttpBridgeAddresses: [
50
+ '0x48C5417ED570928eC85D5e3AD4e7E0EeD7dB1E2A',
51
+ ],
52
+ cctpTokenMessengerAddress: '0x2B4069517957735bE00ceE0fadAE88a26365528f',
53
+ chainId: 10,
54
+ // TODO confirm confirmations and rateLimits
55
+ confirmations: 2,
56
+ rateLimits: {
57
+ blockWindow: 20_000_000_000n,
58
+ blockWindowSize: 10,
59
+ tx: 10_000_000_000n,
60
+ },
61
+ },
62
+ Polygon: {
63
+ attenuatedCttpBridgeAddresses: [
64
+ '0x32cb9574650AFF312c80edc4B4343Ff5500767cA',
65
+ ],
66
+ cctpTokenMessengerAddress: '0x9daF8c91AEFAE50b9c0E69629D3F6Ca40cA3B3FE',
67
+ chainId: 137,
68
+ // TODO confirm confirmations and rateLimits
69
+ confirmations: 2,
70
+ rateLimits: {
71
+ blockWindow: 20_000_000_000n,
72
+ blockWindowSize: 10,
73
+ tx: 10_000_000_000n,
74
+ },
75
+ },
76
+ },
77
+ TESTNET: {
78
+ // Arbitrum Sepolia
79
+ Arbitrum: {
80
+ attenuatedCttpBridgeAddresses: ['0xTODO'],
81
+ cctpTokenMessengerAddress: '0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5',
82
+ chainId: 421614,
83
+ confirmations: 2,
84
+ rateLimits: {
85
+ blockWindow: 20_000_000_000n,
86
+ blockWindowSize: 10,
87
+ tx: 10_000_000_000n,
88
+ },
89
+ },
90
+ // Base Sepolia
91
+ Base: {
92
+ attenuatedCttpBridgeAddresses: ['0xTODO'],
93
+ cctpTokenMessengerAddress: '0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5',
94
+ chainId: 84532,
95
+ confirmations: 2,
96
+ rateLimits: {
97
+ blockWindow: 20_000_000_000n,
98
+ blockWindowSize: 10,
99
+ tx: 10_000_000_000n,
100
+ },
101
+ },
102
+ // Ethereum Sepolia
103
+ Ethereum: {
104
+ attenuatedCttpBridgeAddresses: ['0xTODO'],
105
+ cctpTokenMessengerAddress: '0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5',
106
+ chainId: 11155111,
107
+ confirmations: 2,
108
+ rateLimits: {
109
+ blockWindow: 20_000_000_000n,
110
+ blockWindowSize: 10,
111
+ tx: 10_000_000_000n,
112
+ },
113
+ },
114
+ // OP Sepolia
115
+ Optimism: {
116
+ attenuatedCttpBridgeAddresses: ['0xTODO'],
117
+ cctpTokenMessengerAddress: '0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5',
118
+ chainId: 11155420,
119
+ confirmations: 2,
120
+ rateLimits: {
121
+ blockWindow: 20_000_000_000n,
122
+ blockWindowSize: 10,
123
+ tx: 10_000_000_000n,
124
+ },
125
+ },
126
+ // Polygon PoS Amoy
127
+ Polygon: {
128
+ attenuatedCttpBridgeAddresses: ['0xTODO'],
129
+ cctpTokenMessengerAddress: '0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5',
130
+ chainId: 80002,
131
+ confirmations: 2,
132
+ rateLimits: {
133
+ blockWindow: 20_000_000_000n,
134
+ blockWindowSize: 10,
135
+ tx: 10_000_000_000n,
136
+ },
137
+ },
138
+ },
139
+ });
140
+ harden(ChainPolicies);
@@ -0,0 +1,73 @@
1
+ import { deeplyFulfilledObject, makeTracer, objectMap } from '@agoric/internal';
2
+ import { Fail } from '@endo/errors';
3
+ import { E } from '@endo/eventual-send';
4
+ import { makeMarshal } from '@endo/marshal';
5
+
6
+ const trace = makeTracer('FUCoreEval');
7
+
8
+ /**
9
+ * @import {Brand, DepositFacet} from '@agoric/ertp';
10
+ * @import {FastUSDCKit} from '../start-fast-usdc.core.js'
11
+ * @import {FeedPolicy} from '../types.js'
12
+ */
13
+
14
+ export const FEED_POLICY = 'feedPolicy';
15
+ export const marshalData = makeMarshal(_val => Fail`data only`);
16
+
17
+ /**
18
+ * @param {ERef<StorageNode>} node
19
+ * @param {FeedPolicy} policy
20
+ */
21
+ export const publishFeedPolicy = async (node, policy) => {
22
+ const feedPolicy = E(node).makeChildNode(FEED_POLICY);
23
+ const value = marshalData.toCapData(policy);
24
+ await E(feedPolicy).setValue(JSON.stringify(value));
25
+ };
26
+
27
+ /**
28
+ * @param {object} powers
29
+ * @param {FastUSDCKit['creatorFacet']} powers.creatorFacet
30
+ * @param {BootstrapPowers['consume']['namesByAddress']} powers.namesByAddress
31
+ * @param {Record<string, string>} oracles
32
+ */
33
+ export const inviteOracles = async (
34
+ { creatorFacet, namesByAddress },
35
+ oracles,
36
+ ) => {
37
+ const oracleDepositFacets = await deeplyFulfilledObject(
38
+ objectMap(
39
+ oracles,
40
+ /** @type {(address: string) => Promise<DepositFacet>} */
41
+ address => E(namesByAddress).lookup(address, 'depositFacet'),
42
+ ),
43
+ );
44
+ await Promise.all(
45
+ Object.entries(oracleDepositFacets).map(async ([name, depositFacet]) => {
46
+ const address = oracles[name];
47
+ trace('making invitation for', name, address);
48
+ const toWatch = await E(creatorFacet).makeOperatorInvitation(address);
49
+
50
+ const amt = await E(depositFacet).receive(toWatch);
51
+ trace('sent', amt, 'to', name);
52
+ }),
53
+ );
54
+ };
55
+
56
+ const BOARD_AUX = 'boardAux';
57
+ /**
58
+ * @param {Brand} brand
59
+ * @param {Pick<BootstrapPowers['consume'], 'board' | 'chainStorage'>} powers
60
+ */
61
+ export const publishDisplayInfo = async (brand, { board, chainStorage }) => {
62
+ // chainStorage type includes undefined, which doesn't apply here.
63
+ // @ts-expect-error UNTIL https://github.com/Agoric/agoric-sdk/issues/8247
64
+ const boardAux = E(chainStorage).makeChildNode(BOARD_AUX);
65
+ const [id, displayInfo, allegedName] = await Promise.all([
66
+ E(board).getId(brand),
67
+ E(brand).getDisplayInfo(),
68
+ E(brand).getAllegedName(),
69
+ ]);
70
+ const node = E(boardAux).makeChildNode(id);
71
+ const aux = marshalData.toCapData(harden({ allegedName, displayInfo }));
72
+ await E(node).setValue(JSON.stringify(aux));
73
+ };
@@ -0,0 +1,127 @@
1
+ import { denomHash, withChainCapabilities } from '@agoric/orchestration';
2
+ import fetchedChainInfo from '@agoric/orchestration/src/fetched-chain-info.js';
3
+ import { ChainPolicies } from './chain-policies.js';
4
+
5
+ /**
6
+ * @import {FastUSDCConfig} from '@agoric/fast-usdc';
7
+ * @import {Passable} from '@endo/marshal';
8
+ * @import {CosmosChainInfo, Denom, DenomDetail} from '@agoric/orchestration';
9
+ */
10
+
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
+ /** @type {[Denom, DenomDetail & { brandKey?: string}][]} */
23
+ export const transferAssetInfo = [
24
+ ['uusdc', { baseName: 'noble', chainName: 'noble', baseDenom: 'uusdc' }],
25
+ usdcOnAgoric,
26
+ ];
27
+ harden(transferAssetInfo);
28
+
29
+ /** ABI for DepositForBurn event in TokenMessenger contract */
30
+ const DepositForBurnEvent =
31
+ 'DepositForBurn(uint64,address,uint256,address,bytes32,uint32,bytes32,bytes32)';
32
+
33
+ /**
34
+ * @type {Record<string, Pick<FastUSDCConfig, 'oracles' | 'feedPolicy' | 'chainInfo' | 'assetInfo' >>}
35
+ *
36
+ * meanwhile, use price oracle addresses (from updatePriceFeeds.js).
37
+ */
38
+ export const configurations = {
39
+ /**
40
+ * NOTE: The a3p-integration runtime does _not_ include
41
+ * a noble chain; this limits functionality to advancing
42
+ * to the Agoric chain.
43
+ */
44
+ A3P_INTEGRATION: {
45
+ oracles: {
46
+ gov1: 'agoric1ee9hr0jyrxhy999y755mp862ljgycmwyp4pl7q',
47
+ gov2: 'agoric1wrfh296eu2z34p6pah7q04jjuyj3mxu9v98277',
48
+ gov3: 'agoric1ydzxwh6f893jvpaslmaz6l8j2ulup9a7x8qvvq',
49
+ },
50
+ feedPolicy: {
51
+ nobleAgoricChannelId: 'channel-does-not-exist',
52
+ nobleDomainId: 4,
53
+ chainPolicies: ChainPolicies.TESTNET,
54
+ eventFilter: DepositForBurnEvent,
55
+ },
56
+ chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
57
+ withChainCapabilities({
58
+ agoric: fetchedChainInfo.agoric,
59
+ // registering USDC-on-agoric requires registering the noble chain
60
+ noble: fetchedChainInfo.noble,
61
+ })
62
+ ),
63
+ assetInfo: [usdcOnAgoric],
64
+ },
65
+ MAINNET: {
66
+ // per JVC 12 Feb 2025
67
+ oracles: {
68
+ '01node': 'agoric1ym488t6j24x3ys3va3452ftx44lhs64rz8pu7h',
69
+ SimplyStaking: 'agoric1s5yawjgj6xcw4ea5r2x4cjrnkprmd0fcun2tyk',
70
+ DSRV: 'agoric17crpkfxarq658e9ddru2petrfr0fhjzvjfccq9',
71
+ },
72
+ feedPolicy: {
73
+ nobleAgoricChannelId: 'channel-21',
74
+ nobleDomainId: 4,
75
+ chainPolicies: ChainPolicies.MAINNET,
76
+ eventFilter: DepositForBurnEvent,
77
+ },
78
+ chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
79
+ withChainCapabilities(fetchedChainInfo)
80
+ ),
81
+ assetInfo: transferAssetInfo,
82
+ },
83
+ DEVNET: {
84
+ oracles: {
85
+ DSRV: 'agoric1lw4e4aas9q84tq0q92j85rwjjjapf8dmnllnft',
86
+ Stakin: 'agoric1zj6vrrrjq4gsyr9lw7dplv4vyejg3p8j2urm82',
87
+ '01node': 'agoric1ra0g6crtsy6r3qnpu7ruvm7qd4wjnznyzg5nu4',
88
+ 'Simply Staking': 'agoric1qj07c7vfk3knqdral0sej7fa6eavkdn8vd8etf',
89
+ P2P: 'agoric10vjkvkmpp9e356xeh6qqlhrny2htyzp8hf88fk',
90
+ },
91
+ feedPolicy: {
92
+ nobleAgoricChannelId: 'TODO',
93
+ nobleDomainId: 4,
94
+ chainPolicies: ChainPolicies.TESTNET,
95
+ eventFilter: DepositForBurnEvent,
96
+ },
97
+ chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
98
+ withChainCapabilities(fetchedChainInfo) // TODO: use devnet values
99
+ ),
100
+ assetInfo: transferAssetInfo,
101
+ },
102
+ EMERYNET: {
103
+ oracles: {
104
+ gov1: 'agoric1ldmtatp24qlllgxmrsjzcpe20fvlkp448zcuce',
105
+ gov2: 'agoric140dmkrz2e42ergjj7gyvejhzmjzurvqeq82ang',
106
+ },
107
+ feedPolicy: {
108
+ nobleAgoricChannelId: 'TODO',
109
+ nobleDomainId: 4,
110
+ chainPolicies: ChainPolicies.TESTNET,
111
+ eventFilter: DepositForBurnEvent,
112
+ },
113
+ chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
114
+ withChainCapabilities(fetchedChainInfo) // TODO: use emerynet values
115
+ ),
116
+ assetInfo: transferAssetInfo,
117
+ },
118
+ };
119
+ harden(configurations);
120
+
121
+ // Constraints on the configurations
122
+ const MAINNET_EXPECTED_ORACLES = 3;
123
+ assert(
124
+ new Set(Object.values(configurations.MAINNET.oracles)).size ===
125
+ MAINNET_EXPECTED_ORACLES,
126
+ `Mainnet must have exactly ${MAINNET_EXPECTED_ORACLES} oracles`,
127
+ );
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, min, subtract } = AmountMath;
7
+ const { add, isGTE, subtract } = AmountMath;
8
8
 
9
9
  /**
10
10
  * @import {Amount} from '@agoric/ertp';
@@ -15,7 +15,7 @@ const { add, isGTE, min, 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, maxVariable } = feeConfig;
18
+ const { flat, variableRate } = feeConfig;
19
19
  const feeTools = harden({
20
20
  /**
21
21
  * Calculate the net amount to advance after withholding fees.
@@ -34,8 +34,7 @@ export const makeFeeTools = feeConfig => {
34
34
  * @throws {Error} if requested does not exceed fees
35
35
  */
36
36
  calculateAdvanceFee(requested) {
37
- const variable = min(multiplyBy(requested, variableRate), maxVariable);
38
- const fee = add(variable, flat);
37
+ const fee = add(multiplyBy(requested, variableRate), flat);
39
38
  !isGTE(fee, requested) || Fail`Request must exceed fees.`;
40
39
  return fee;
41
40
  },
@@ -0,0 +1,9 @@
1
+ export const flags = (
2
+ record: Record<string, string | number | bigint | undefined>,
3
+ ): string[] => {
4
+ // @ts-expect-error undefined is filtered out
5
+ const skipUndef: [string, string][] = Object.entries(record).filter(
6
+ ([_k, v]) => v !== undefined,
7
+ );
8
+ return skipUndef.map(([k, v]) => [`--${k}`, `${v}`]).flat();
9
+ };
@@ -0,0 +1,14 @@
1
+ import type { Writable } from 'node:stream';
2
+
3
+ /**
4
+ * mock stdout / stderr, for example
5
+ *
6
+ * @param buf - caller-provided buffer for written data
7
+ */
8
+ export const mockStream = <T extends Writable>(buf: string[]): T =>
9
+ ({
10
+ write: txt => {
11
+ buf.push(txt);
12
+ return true;
13
+ },
14
+ }) as T;
@@ -1,26 +0,0 @@
1
- ## **StatusManager** state diagram, showing different transitions
2
-
3
-
4
- ### Contract state diagram
5
-
6
- *Transactions are qualified by the OCW and EventFeed before arriving to the Advancer.*
7
-
8
- ```mermaid
9
- stateDiagram-v2
10
- [*] --> Advanced: Advancer .advance()
11
- Advanced --> Settled: Settler .settle() after fees
12
- [*] --> Observed: Advancer .observed()
13
- Observed --> Settled: Settler .settle() sans fees
14
- Settled --> [*]
15
- ```
16
-
17
- ### Complete state diagram (starting from OCW)
18
-
19
- ```mermaid
20
- stateDiagram-v2
21
- Observed --> Qualified
22
- Observed --> Unqualified
23
- Qualified --> Advanced
24
- Advanced --> Settled
25
- Qualified --> Settled
26
- ```
@@ -1,71 +0,0 @@
1
- import { makeError, q } from '@endo/errors';
2
- import { M, mustMatch } from '@endo/patterns';
3
-
4
- /**
5
- * @import {Pattern} from '@endo/patterns';
6
- */
7
-
8
- /**
9
- * Default pattern matcher for `getQueryParams`.
10
- * Does not assert keys exist, but ensures existing keys are strings.
11
- */
12
- const QueryParamsShape = M.splitRecord(
13
- {},
14
- {},
15
- M.recordOf(M.string(), M.string()),
16
- );
17
-
18
- /**
19
- * Very minimal 'URL query string'-like parser that handles:
20
- * - Query string delimiter (?)
21
- * - Key-value separator (=)
22
- * - Query parameter separator (&)
23
- *
24
- * Does not handle:
25
- * - Subpaths (`agoric1bech32addr/opt/account?k=v`)
26
- * - URI encoding/decoding (`%20` -> ` `)
27
- * - note: `decodeURIComponent` seems to be available in XS
28
- * - Multiple question marks (foo?bar=1?baz=2)
29
- * - Empty parameters (foo=)
30
- * - Array parameters (`foo?k=v1&k=v2` -> k: [v1, v2])
31
- * - Parameters without values (foo&bar=2)
32
- */
33
- export const addressTools = {
34
- /**
35
- * @param {string} address
36
- * @returns {boolean}
37
- */
38
- hasQueryParams: address => {
39
- try {
40
- const params = addressTools.getQueryParams(address);
41
- return Object.keys(params).length > 0;
42
- } catch {
43
- return false;
44
- }
45
- },
46
- /**
47
- * @param {string} address
48
- * @param {Pattern} [shape]
49
- * @returns {Record<string, string>}
50
- * @throws {Error} if the address cannot be parsed or params do not match `shape`
51
- */
52
- getQueryParams: (address, shape = QueryParamsShape) => {
53
- const parts = address.split('?');
54
- if (parts.length !== 2) {
55
- throw makeError(`Unable to parse query params: ${q(address)}`);
56
- }
57
- /** @type {Record<string, string>} */
58
- const result = {};
59
- const paramPairs = parts[1].split('&');
60
- for (const pair of paramPairs) {
61
- const [key, value] = pair.split('=');
62
- if (!key || !value) {
63
- throw makeError(`Invalid parameter format in pair: ${q(pair)}`);
64
- }
65
- result[key] = value;
66
- }
67
- harden(result);
68
- mustMatch(result, shape);
69
- return result;
70
- },
71
- };
File without changes