@agoric/fast-usdc 0.1.1-other-dev-3eb1a1d.0 → 0.2.0-u18.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/README.md CHANGED
@@ -56,3 +56,46 @@ sequenceDiagram
56
56
 
57
57
  A->>TF: notify(evidence)
58
58
  ```
59
+
60
+ # Status Manager
61
+
62
+ ### Pending Advance State Diagram
63
+
64
+ *Transactions are qualified by the OCW and EventFeed before arriving to the Advancer.*
65
+
66
+ ```mermaid
67
+ stateDiagram-v2
68
+ [*] --> Observed: observe()
69
+ [*] --> Advancing: advancing()
70
+
71
+ Advancing --> Advanced: advanceOutcome(...true)
72
+ Advancing --> AdvanceFailed: advanceOutcome(...false)
73
+
74
+ Observed --> [*]: dequeueStatus()
75
+ Advanced --> [*]: dequeueStatus()
76
+ AdvanceFailed --> [*]: dequeueStatus()
77
+
78
+ note right of [*]
79
+ After dequeueStatus():
80
+ Transaction is removed
81
+ from pendingTxs store.
82
+ Settler will .disburse()
83
+ or .forward()
84
+ end note
85
+ ```
86
+
87
+ ### Complete state diagram (starting from Transaction Feed into Advancer)
88
+
89
+ ```mermaid
90
+ stateDiagram-v2
91
+ Observed --> AdvanceSkipped : Risks identified
92
+ Observed --> Advancing : No risks, can advance
93
+ Observed --> Forwarding : No risks, Mint deposited before advance
94
+ Forwarding --> Forwarded
95
+ Advancing --> Advanced
96
+ Advanced --> Disbursed
97
+ AdvanceSkipped --> Forwarding : Mint deposited
98
+ AdvanceFailed --> Forwarding : Mint deposited
99
+ Advancing --> AdvanceFailed
100
+ Forwarding --> ForwardFailed
101
+ ```
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@agoric/fast-usdc",
3
- "version": "0.1.1-other-dev-3eb1a1d.0+3eb1a1d",
3
+ "version": "0.2.0-u18.0",
4
4
  "description": "CLI and library for Fast USDC product",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "contract",
8
- "src"
8
+ "src",
9
+ "tools"
9
10
  ],
10
11
  "bin": {
11
12
  "fast-usdc": "./src/cli/bin.js"
@@ -21,25 +22,26 @@
21
22
  "lint:eslint": "eslint ."
22
23
  },
23
24
  "devDependencies": {
24
- "@agoric/swingset-liveslots": "0.10.3-other-dev-3eb1a1d.0+3eb1a1d",
25
- "@agoric/vats": "0.15.2-other-dev-3eb1a1d.0+3eb1a1d",
26
- "@agoric/zone": "0.2.3-other-dev-3eb1a1d.0+3eb1a1d",
25
+ "@agoric/swingset-liveslots": "^0.10.3-u18.1",
26
+ "@agoric/vats": "^0.16.0-u18.5",
27
+ "@agoric/zone": "^0.3.0-u18.1",
27
28
  "@fast-check/ava": "^2.0.1",
28
29
  "ava": "^5.3.0",
29
30
  "c8": "^10.1.2",
30
31
  "execa": "9.1.0",
31
- "ts-blank-space": "^0.4.1"
32
+ "ts-blank-space": "^0.4.4"
32
33
  },
33
34
  "dependencies": {
34
- "@agoric/client-utils": "0.1.1-other-dev-3eb1a1d.0+3eb1a1d",
35
- "@agoric/ertp": "0.16.3-other-dev-3eb1a1d.0+3eb1a1d",
36
- "@agoric/internal": "0.3.3-other-dev-3eb1a1d.0+3eb1a1d",
37
- "@agoric/notifier": "0.6.3-other-dev-3eb1a1d.0+3eb1a1d",
38
- "@agoric/orchestration": "0.1.1-other-dev-3eb1a1d.0+3eb1a1d",
39
- "@agoric/store": "0.9.3-other-dev-3eb1a1d.0+3eb1a1d",
40
- "@agoric/vat-data": "0.5.3-other-dev-3eb1a1d.0+3eb1a1d",
41
- "@agoric/vow": "0.1.1-other-dev-3eb1a1d.0+3eb1a1d",
42
- "@agoric/zoe": "0.26.3-other-dev-3eb1a1d.0+3eb1a1d",
35
+ "@agoric/client-utils": "^0.2.0-u18.0",
36
+ "@agoric/cosmic-proto": "^0.5.0-u18.5",
37
+ "@agoric/ertp": "^0.16.3-u18.1",
38
+ "@agoric/internal": "^0.4.0-u18.1",
39
+ "@agoric/notifier": "^0.7.0-u18.1",
40
+ "@agoric/orchestration": "^0.2.0-u18.5",
41
+ "@agoric/store": "^0.9.3-u18.1",
42
+ "@agoric/vat-data": "^0.5.3-u18.1",
43
+ "@agoric/vow": "^0.2.0-u18.1",
44
+ "@agoric/zoe": "^0.26.3-u18.1",
43
45
  "@cosmjs/proto-signing": "^0.32.4",
44
46
  "@cosmjs/stargate": "^0.32.4",
45
47
  "@endo/base64": "^1.0.9",
@@ -49,6 +51,7 @@
49
51
  "@endo/far": "^1.1.9",
50
52
  "@endo/init": "^1.1.7",
51
53
  "@endo/marshal": "^1.6.2",
54
+ "@endo/nat": "^5.0.13",
52
55
  "@endo/pass-style": "^1.4.7",
53
56
  "@endo/patterns": "^1.4.7",
54
57
  "@endo/promise-kit": "^1.1.8",
@@ -78,5 +81,5 @@
78
81
  "publishConfig": {
79
82
  "access": "public"
80
83
  },
81
- "gitHead": "3eb1a1d2d75b2b4a94807cd3bf759bc9fc531f05"
84
+ "gitHead": "f8c45b8a2e29a51522a81a6692af25b2d7f6b50f"
82
85
  }
@@ -0,0 +1,40 @@
1
+ import { boardSlottingMarshaller } from '@agoric/client-utils';
2
+
3
+ /**
4
+ * @import {BridgeAction} from '@agoric/smart-wallet/src/smartWallet.js';
5
+ */
6
+
7
+ const defaultMarshaller = boardSlottingMarshaller();
8
+
9
+ /** @typedef {ReturnType<boardSlottingMarshaller>} BoardSlottingMarshaller */
10
+
11
+ /**
12
+ * @param {BridgeAction} bridgeAction
13
+ * @param {Pick<import('stream').Writable,'write'>} stdout
14
+ * @param {BoardSlottingMarshaller} marshaller
15
+ */
16
+ const outputAction = (bridgeAction, stdout, marshaller) => {
17
+ const capData = marshaller.toCapData(harden(bridgeAction));
18
+ stdout.write(JSON.stringify(capData));
19
+ stdout.write('\n');
20
+ };
21
+
22
+ export const sendHint =
23
+ 'Now use `agoric wallet send ...` to sign and broadcast the offer.\n';
24
+
25
+ /**
26
+ * @param {BridgeAction} bridgeAction
27
+ * @param {{
28
+ * stdout: Pick<import('stream').Writable,'write'>,
29
+ * stderr: Pick<import('stream').Writable,'write'>,
30
+ * }} io
31
+ * @param {BoardSlottingMarshaller | undefined} marshaller
32
+ */
33
+ export const outputActionAndHint = (
34
+ bridgeAction,
35
+ { stdout, stderr },
36
+ marshaller = defaultMarshaller,
37
+ ) => {
38
+ outputAction(bridgeAction, stdout, marshaller);
39
+ stderr.write(sendHint);
40
+ };
package/src/cli/cli.js CHANGED
@@ -1,9 +1,6 @@
1
- import { assertParsableNumber } from '@agoric/zoe/src/contractSupport/ratio.js';
2
- import {
3
- Command,
4
- InvalidArgumentError,
5
- InvalidOptionArgumentError,
6
- } from 'commander';
1
+ /* eslint-env node */
2
+ /* global globalThis */
3
+ import { Command } from 'commander';
7
4
  import { existsSync, mkdirSync, readFileSync } from 'fs';
8
5
  import { fileURLToPath } from 'url';
9
6
  import { dirname, resolve } from 'path';
@@ -12,9 +9,12 @@ import {
12
9
  readFile as readAsync,
13
10
  writeFile as writeAsync,
14
11
  } from 'node:fs/promises';
15
- import configLib from './config.js';
12
+ import { addConfigCommands } from './config-commands.js';
13
+ import { addOperatorCommands } from './operator-commands.js';
14
+ import * as configLib from './config.js';
16
15
  import transferLib from './transfer.js';
17
16
  import { makeFile } from '../util/file.js';
17
+ import { addLPCommands } from './lp-commands.js';
18
18
 
19
19
  const packageJson = JSON.parse(
20
20
  readFileSync(
@@ -32,6 +32,11 @@ export const initProgram = (
32
32
  writeFile = writeAsync,
33
33
  mkdir = mkdirSync,
34
34
  exists = existsSync,
35
+ fetch = globalThis.fetch,
36
+ stdout = process.stdout,
37
+ stderr = process.stderr,
38
+ env = process.env,
39
+ now = () => Date.now(),
35
40
  ) => {
36
41
  const program = new Command();
37
42
 
@@ -45,153 +50,36 @@ export const initProgram = (
45
50
  `${defaultHome}/.fast-usdc/`,
46
51
  );
47
52
 
48
- const config = program.command('config').description('Manage config');
49
-
50
- const configFilename = 'config.json';
51
- const getConfigPath = () => {
52
- const { home: configDir } = program.opts();
53
- return configDir + configFilename;
54
- };
55
-
56
- const makeConfigFile = () =>
57
- makeFile(getConfigPath(), readFile, writeFile, mkdir, exists);
58
-
59
- config
60
- .command('show')
61
- .description('Show current config')
62
- .action(async () => {
63
- await configHelpers.show(makeConfigFile());
64
- });
65
-
66
- config
67
- .command('init')
68
- .description('Set initial config values')
69
- .requiredOption(
70
- '--noble-seed <seed>',
71
- 'Seed phrase for Noble account. CAUTION: Stored unencrypted in file system',
72
- )
73
- .requiredOption(
74
- '--eth-seed <seed>',
75
- 'Seed phrase for Ethereum account. CAUTION: Stored unencrypted in file system',
76
- )
77
- .requiredOption(
78
- '--agoric-seed <seed>',
79
- 'Seed phrase for Agoric LP account. CAUTION: Stored unencrypted in file system',
80
- )
81
- .option(
82
- '--agoric-rpc [url]',
83
- 'Agoric RPC endpoint',
84
- 'http://127.0.0.1:26656',
85
- )
86
- .option(
87
- '--agoric-api [url]',
88
- 'Agoric RPC endpoint',
89
- 'http://127.0.0.1:1317',
90
- )
91
- .option('--noble-rpc [url]', 'Noble RPC endpoint', 'http://127.0.0.1:26657')
92
- .option('--noble-api [url]', 'Noble API endpoint', 'http://127.0.0.1:1318')
93
- .option('--eth-rpc [url]', 'Ethereum RPC Endpoint', 'http://127.0.0.1:8545')
94
- .option(
95
- '--noble-to-agoric-channel [channel]',
96
- 'Channel ID on Noble for Agoric',
97
- 'channel-21',
98
- )
99
- .option(
100
- '--token-messenger-address [address]',
101
- 'Address of TokenMessenger contract',
102
- // Default to ETH mainnet contract address. For ETH sepolia, use 0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5
103
- '0xbd3fa81b58ba92a82136038b25adec7066af3155',
104
- )
105
- .option(
106
- '--token-contract-address [address]',
107
- 'Address of USDC token contract',
108
- // Detault to ETH mainnet token address. For ETH sepolia, use 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238
109
- '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
110
- )
111
- .action(async options => {
112
- await configHelpers.init(makeConfigFile(), options);
113
- });
114
-
115
- config
116
- .command('update')
117
- .description('Update config values')
118
- .option(
119
- '--noble-seed [string]',
120
- 'Seed phrase for Noble account. CAUTION: Stored unencrypted in file system',
121
- )
122
- .option(
123
- '--eth-seed [string]',
124
- 'Seed phrase for Ethereum account. CAUTION: Stored unencrypted in file system',
125
- )
126
- .option(
127
- '--agoric-seed <seed>',
128
- 'Seed phrase for Agoric LP account. CAUTION: Stored unencrypted in file system',
129
- )
130
- .option('--agoric-rpc [url]', 'Agoric RPC endpoint')
131
- .option('--agoric-api [url]', 'Agoric API endpoint')
132
- .option('--noble-rpc [url]', 'Noble RPC endpoint')
133
- .option('--noble-api [url]', 'Noble API endpoint')
134
- .option('--eth-rpc [url]', 'Ethereum RPC Endpoint')
135
- .option(
136
- '--noble-to-agoric-channel [channel]',
137
- 'Channel ID on Noble for Agoric',
138
- )
139
- .option(
140
- '--token-messenger-address [address]',
141
- 'Address of TokenMessenger contract',
142
- )
143
- .option(
144
- '--token-contract-address [address]',
145
- 'Address of USDC token contract',
146
- )
147
- .action(async options => {
148
- await configHelpers.update(makeConfigFile(), options);
149
- });
150
-
151
- /** @param {string} value */
152
- const parseDecimal = value => {
153
- try {
154
- assertParsableNumber(value);
155
- } catch {
156
- throw new InvalidArgumentError('Not a decimal number.');
157
- }
158
- return value;
159
- };
160
-
161
- /**
162
- * @param {string} str
163
- * @returns {'auto' | number}
164
- */
165
- const parseFee = str => {
166
- if (str === 'auto') return 'auto';
167
- const num = parseFloat(str);
168
- if (Number.isNaN(num)) {
169
- throw new InvalidOptionArgumentError('Fee must be a number.');
170
- }
171
- return num;
53
+ const makeConfigFile = () => {
54
+ const getConfigPath = () => {
55
+ const { home: configDir } = program.opts();
56
+ return `${configDir}config.json`;
57
+ };
58
+ return makeFile(getConfigPath(), readFile, writeFile, mkdir, exists);
172
59
  };
173
60
 
174
- program
175
- .command('deposit')
176
- .description('Offer assets to the liquidity pool')
177
- .argument('<give>', 'USDC to give', parseDecimal)
178
- .option('--id [offer-id]', 'Offer ID')
179
- .option('--fee [fee]', 'Cosmos fee', parseFee)
180
- .action(() => {
181
- console.error('TODO actually send deposit');
182
- // TODO: Implement deposit logic
183
- });
184
-
185
- program
186
- .command('withdraw')
187
- .description('Withdraw assets from the liquidity pool')
188
- .argument('<want>', 'USDC to withdraw', parseDecimal)
189
- .option('--id [offer-id]', 'Offer ID')
190
- .option('--fee [fee]', 'Cosmos fee', parseFee)
191
- .action(() => {
192
- console.error('TODO actually send withdrawal');
193
- // TODO: Implement withdraw logic
194
- });
61
+ program.addHelpText(
62
+ 'afterAll',
63
+ `
64
+ Agoric test networks provide configuration info at, for example,
65
+
66
+ https://devnet.agoric.net/network-config
67
+
68
+ To use RPC endpoints from such a configuration, use:
69
+ export AGORIC_NET=devnet
70
+
71
+ Use AGORIC_NET=local or leave it unset to use localhost and chain id agoriclocal.
72
+ `,
73
+ );
74
+ addConfigCommands(program, configHelpers, makeConfigFile);
75
+ addOperatorCommands(program, {
76
+ fetch,
77
+ stdout,
78
+ stderr,
79
+ env,
80
+ now,
81
+ });
82
+ addLPCommands(program, { fetch, stdout, stderr, env, now });
195
83
 
196
84
  program
197
85
  .command('transfer')
@@ -203,7 +91,12 @@ export const initProgram = (
203
91
  /** @type {string} */ amount,
204
92
  /** @type {string} */ destination,
205
93
  ) => {
94
+ const start = now();
206
95
  await transferHelpers.transfer(makeConfigFile(), amount, destination);
96
+ const duration = now() - start;
97
+ stdout.write(
98
+ `Transfer finished in ${(duration / 1000).toFixed(1)} seconds`,
99
+ );
207
100
  },
208
101
  );
209
102
 
@@ -0,0 +1,108 @@
1
+ /**
2
+ * @import {Command} from 'commander';
3
+ * @import {File} from '../util/file.js';
4
+ * @import * as ConfigHelpers from './config.js';
5
+ */
6
+ /**
7
+ *
8
+ * @param {Command} program
9
+ * @param {ConfigHelpers} configHelpers
10
+ * @param {() => File} makeConfigFile
11
+ */
12
+ export const addConfigCommands = (program, configHelpers, makeConfigFile) => {
13
+ const config = program.command('config').description('Manage config');
14
+
15
+ config
16
+ .command('show')
17
+ .description('Show current config')
18
+ .action(async () => {
19
+ await configHelpers.show(makeConfigFile());
20
+ });
21
+
22
+ config
23
+ .command('init')
24
+ .description('Set initial config values')
25
+ .requiredOption(
26
+ '--noble-seed <seed>',
27
+ 'Seed phrase for Noble account. CAUTION: Stored unencrypted in file system',
28
+ )
29
+ .requiredOption(
30
+ '--eth-seed <seed>',
31
+ 'Seed phrase for Ethereum account. CAUTION: Stored unencrypted in file system',
32
+ )
33
+ .requiredOption(
34
+ '--agoric-seed <seed>',
35
+ 'Seed phrase for Agoric LP account. CAUTION: Stored unencrypted in file system',
36
+ )
37
+ .option(
38
+ '--agoric-rpc [url]',
39
+ 'Agoric RPC endpoint',
40
+ 'http://127.0.0.1:26656',
41
+ )
42
+ .option(
43
+ '--agoric-api [url]',
44
+ 'Agoric RPC endpoint',
45
+ 'http://127.0.0.1:1317',
46
+ )
47
+ .option('--noble-rpc [url]', 'Noble RPC endpoint', 'http://127.0.0.1:26657')
48
+ .option('--noble-api [url]', 'Noble API endpoint', 'http://127.0.0.1:1318')
49
+ .option('--eth-rpc [url]', 'Ethereum RPC Endpoint', 'http://127.0.0.1:8545')
50
+ .option(
51
+ '--noble-to-agoric-channel [channel]',
52
+ 'Channel ID on Noble for Agoric',
53
+ 'channel-21',
54
+ )
55
+ .option(
56
+ '--token-messenger-address [address]',
57
+ 'Address of TokenMessenger contract',
58
+ // Default to ETH mainnet contract address. For ETH sepolia, use 0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5
59
+ '0xbd3fa81b58ba92a82136038b25adec7066af3155',
60
+ )
61
+ .option(
62
+ '--token-contract-address [address]',
63
+ 'Address of USDC token contract',
64
+ // Detault to ETH mainnet token address. For ETH sepolia, use 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238
65
+ '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
66
+ )
67
+ .action(async options => {
68
+ await configHelpers.init(makeConfigFile(), options);
69
+ });
70
+
71
+ config
72
+ .command('update')
73
+ .description('Update config values')
74
+ .option(
75
+ '--noble-seed [string]',
76
+ 'Seed phrase for Noble account. CAUTION: Stored unencrypted in file system',
77
+ )
78
+ .option(
79
+ '--eth-seed [string]',
80
+ 'Seed phrase for Ethereum account. CAUTION: Stored unencrypted in file system',
81
+ )
82
+ .option(
83
+ '--agoric-seed <seed>',
84
+ 'Seed phrase for Agoric LP account. CAUTION: Stored unencrypted in file system',
85
+ )
86
+ .option('--agoric-rpc [url]', 'Agoric RPC endpoint')
87
+ .option('--agoric-api [url]', 'Agoric API endpoint')
88
+ .option('--noble-rpc [url]', 'Noble RPC endpoint')
89
+ .option('--noble-api [url]', 'Noble API endpoint')
90
+ .option('--eth-rpc [url]', 'Ethereum RPC Endpoint')
91
+ .option(
92
+ '--noble-to-agoric-channel [channel]',
93
+ 'Channel ID on Noble for Agoric',
94
+ )
95
+ .option(
96
+ '--token-messenger-address [address]',
97
+ 'Address of TokenMessenger contract',
98
+ )
99
+ .option(
100
+ '--token-contract-address [address]',
101
+ 'Address of USDC token contract',
102
+ )
103
+ .action(async options => {
104
+ await configHelpers.update(makeConfigFile(), options);
105
+ });
106
+
107
+ return config;
108
+ };
package/src/cli/config.js CHANGED
@@ -1,24 +1,32 @@
1
1
  import * as readline from 'node:readline/promises';
2
2
  import { stdin as input, stdout as output } from 'node:process';
3
3
 
4
+ /**
5
+ @typedef {{
6
+ bech32Prefix: string,
7
+ api: string,
8
+ USDCDenom: string
9
+ }} DestinationChain
10
+ */
11
+
4
12
  /**
5
13
  @typedef {{
6
14
  nobleSeed: string,
7
15
  ethSeed: string,
8
16
  nobleToAgoricChannel: string,
9
- agoricRpc: string,
10
17
  nobleApi: string,
11
18
  nobleRpc: string,
12
19
  ethRpc: string,
13
20
  tokenMessengerAddress: string,
14
21
  tokenAddress: string
22
+ destinationChains?: DestinationChain[]
15
23
  }} ConfigOpts
16
24
  */
17
25
 
18
- /** @import { file } from '../util/file' */
26
+ /** @import { File } from '../util/file' */
19
27
 
20
- const init = async (
21
- /** @type {file} */ configFile,
28
+ export const init = async (
29
+ /** @type {File} */ configFile,
22
30
  /** @type {ConfigOpts} */ options,
23
31
  out = console,
24
32
  rl = readline.createInterface({ input, output }),
@@ -52,8 +60,8 @@ const init = async (
52
60
  await writeConfig();
53
61
  };
54
62
 
55
- const update = async (
56
- /** @type {file} */ configFile,
63
+ export const update = async (
64
+ /** @type {File} */ configFile,
57
65
  /** @type {Partial<ConfigOpts>} */ options,
58
66
  out = console,
59
67
  ) => {
@@ -83,7 +91,7 @@ const update = async (
83
91
  await updateConfig({ ...JSON.parse(file), ...options });
84
92
  };
85
93
 
86
- const show = async (/** @type {file} */ configFile, out = console) => {
94
+ export const show = async (/** @type {File} */ configFile, out = console) => {
87
95
  let contents;
88
96
  await null;
89
97
  try {
@@ -97,5 +105,3 @@ const show = async (/** @type {file} */ configFile, out = console) => {
97
105
  out.log(`Config found at ${configFile.path}:`);
98
106
  out.log(contents);
99
107
  };
100
-
101
- export default { init, update, show };