@agoric/fast-usdc 0.1.1-dev-340ef0d.0 → 0.1.1-dev-c1ae023.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 +14 -14
- package/src/cli/bridge-action.js +12 -4
- package/src/cli/cli.js +3 -51
- package/src/cli/lp-commands.js +171 -0
- package/src/cli/operator-commands.js +4 -0
- package/src/exos/status-manager.js +53 -35
- package/src/fast-usdc.contract.js +2 -2
- package/src/types.ts +0 -6
- package/src/util/agoric.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/fast-usdc",
|
|
3
|
-
"version": "0.1.1-dev-
|
|
3
|
+
"version": "0.1.1-dev-c1ae023.0+c1ae023",
|
|
4
4
|
"description": "CLI and library for Fast USDC product",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"lint:eslint": "eslint ."
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@agoric/swingset-liveslots": "0.10.3-dev-
|
|
26
|
-
"@agoric/vats": "0.15.2-dev-
|
|
27
|
-
"@agoric/zone": "0.2.3-dev-
|
|
25
|
+
"@agoric/swingset-liveslots": "0.10.3-dev-c1ae023.0+c1ae023",
|
|
26
|
+
"@agoric/vats": "0.15.2-dev-c1ae023.0+c1ae023",
|
|
27
|
+
"@agoric/zone": "0.2.3-dev-c1ae023.0+c1ae023",
|
|
28
28
|
"@fast-check/ava": "^2.0.1",
|
|
29
29
|
"ava": "^5.3.0",
|
|
30
30
|
"c8": "^10.1.2",
|
|
@@ -32,15 +32,15 @@
|
|
|
32
32
|
"ts-blank-space": "^0.4.4"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@agoric/client-utils": "0.1.1-dev-
|
|
36
|
-
"@agoric/ertp": "0.16.3-dev-
|
|
37
|
-
"@agoric/internal": "0.3.3-dev-
|
|
38
|
-
"@agoric/notifier": "0.6.3-dev-
|
|
39
|
-
"@agoric/orchestration": "0.1.1-dev-
|
|
40
|
-
"@agoric/store": "0.9.3-dev-
|
|
41
|
-
"@agoric/vat-data": "0.5.3-dev-
|
|
42
|
-
"@agoric/vow": "0.1.1-dev-
|
|
43
|
-
"@agoric/zoe": "0.26.3-dev-
|
|
35
|
+
"@agoric/client-utils": "0.1.1-dev-c1ae023.0+c1ae023",
|
|
36
|
+
"@agoric/ertp": "0.16.3-dev-c1ae023.0+c1ae023",
|
|
37
|
+
"@agoric/internal": "0.3.3-dev-c1ae023.0+c1ae023",
|
|
38
|
+
"@agoric/notifier": "0.6.3-dev-c1ae023.0+c1ae023",
|
|
39
|
+
"@agoric/orchestration": "0.1.1-dev-c1ae023.0+c1ae023",
|
|
40
|
+
"@agoric/store": "0.9.3-dev-c1ae023.0+c1ae023",
|
|
41
|
+
"@agoric/vat-data": "0.5.3-dev-c1ae023.0+c1ae023",
|
|
42
|
+
"@agoric/vow": "0.1.1-dev-c1ae023.0+c1ae023",
|
|
43
|
+
"@agoric/zoe": "0.26.3-dev-c1ae023.0+c1ae023",
|
|
44
44
|
"@cosmjs/proto-signing": "^0.32.4",
|
|
45
45
|
"@cosmjs/stargate": "^0.32.4",
|
|
46
46
|
"@endo/base64": "^1.0.9",
|
|
@@ -80,5 +80,5 @@
|
|
|
80
80
|
"publishConfig": {
|
|
81
81
|
"access": "public"
|
|
82
82
|
},
|
|
83
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "c1ae023438eb7cdf0c84bc5e21e6b682ec9fbd36"
|
|
84
84
|
}
|
package/src/cli/bridge-action.js
CHANGED
|
@@ -4,13 +4,16 @@ import { boardSlottingMarshaller } from '@agoric/client-utils';
|
|
|
4
4
|
* @import {BridgeAction} from '@agoric/smart-wallet/src/smartWallet.js';
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const defaultMarshaller = boardSlottingMarshaller();
|
|
8
|
+
|
|
9
|
+
/** @typedef {ReturnType<boardSlottingMarshaller>} BoardSlottingMarshaller */
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* @param {BridgeAction} bridgeAction
|
|
11
13
|
* @param {Pick<import('stream').Writable,'write'>} stdout
|
|
14
|
+
* @param {BoardSlottingMarshaller} marshaller
|
|
12
15
|
*/
|
|
13
|
-
const outputAction = (bridgeAction, stdout) => {
|
|
16
|
+
const outputAction = (bridgeAction, stdout, marshaller) => {
|
|
14
17
|
const capData = marshaller.toCapData(harden(bridgeAction));
|
|
15
18
|
stdout.write(JSON.stringify(capData));
|
|
16
19
|
stdout.write('\n');
|
|
@@ -25,8 +28,13 @@ export const sendHint =
|
|
|
25
28
|
* stdout: Pick<import('stream').Writable,'write'>,
|
|
26
29
|
* stderr: Pick<import('stream').Writable,'write'>,
|
|
27
30
|
* }} io
|
|
31
|
+
* @param {BoardSlottingMarshaller | undefined} marshaller
|
|
28
32
|
*/
|
|
29
|
-
export const outputActionAndHint = (
|
|
30
|
-
|
|
33
|
+
export const outputActionAndHint = (
|
|
34
|
+
bridgeAction,
|
|
35
|
+
{ stdout, stderr },
|
|
36
|
+
marshaller = defaultMarshaller,
|
|
37
|
+
) => {
|
|
38
|
+
outputAction(bridgeAction, stdout, marshaller);
|
|
31
39
|
stderr.write(sendHint);
|
|
32
40
|
};
|
package/src/cli/cli.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
/* eslint-env node */
|
|
2
2
|
/* global globalThis */
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
Command,
|
|
6
|
-
InvalidArgumentError,
|
|
7
|
-
InvalidOptionArgumentError,
|
|
8
|
-
} from 'commander';
|
|
3
|
+
import { Command } from 'commander';
|
|
9
4
|
import { existsSync, mkdirSync, readFileSync } from 'fs';
|
|
10
5
|
import { fileURLToPath } from 'url';
|
|
11
6
|
import { dirname, resolve } from 'path';
|
|
@@ -19,6 +14,7 @@ import { addOperatorCommands } from './operator-commands.js';
|
|
|
19
14
|
import * as configLib from './config.js';
|
|
20
15
|
import transferLib from './transfer.js';
|
|
21
16
|
import { makeFile } from '../util/file.js';
|
|
17
|
+
import { addLPCommands } from './lp-commands.js';
|
|
22
18
|
|
|
23
19
|
const packageJson = JSON.parse(
|
|
24
20
|
readFileSync(
|
|
@@ -83,51 +79,7 @@ export const initProgram = (
|
|
|
83
79
|
env,
|
|
84
80
|
now,
|
|
85
81
|
});
|
|
86
|
-
|
|
87
|
-
/** @param {string} value */
|
|
88
|
-
const parseDecimal = value => {
|
|
89
|
-
try {
|
|
90
|
-
assertParsableNumber(value);
|
|
91
|
-
} catch {
|
|
92
|
-
throw new InvalidArgumentError('Not a decimal number.');
|
|
93
|
-
}
|
|
94
|
-
return value;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* @param {string} str
|
|
99
|
-
* @returns {'auto' | number}
|
|
100
|
-
*/
|
|
101
|
-
const parseFee = str => {
|
|
102
|
-
if (str === 'auto') return 'auto';
|
|
103
|
-
const num = parseFloat(str);
|
|
104
|
-
if (Number.isNaN(num)) {
|
|
105
|
-
throw new InvalidOptionArgumentError('Fee must be a number.');
|
|
106
|
-
}
|
|
107
|
-
return num;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
program
|
|
111
|
-
.command('deposit')
|
|
112
|
-
.description('Offer assets to the liquidity pool')
|
|
113
|
-
.argument('<give>', 'USDC to give', parseDecimal)
|
|
114
|
-
.option('--id [offer-id]', 'Offer ID')
|
|
115
|
-
.option('--fee [fee]', 'Cosmos fee', parseFee)
|
|
116
|
-
.action(() => {
|
|
117
|
-
console.error('TODO actually send deposit');
|
|
118
|
-
// TODO: Implement deposit logic
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
program
|
|
122
|
-
.command('withdraw')
|
|
123
|
-
.description('Withdraw assets from the liquidity pool')
|
|
124
|
-
.argument('<want>', 'USDC to withdraw', parseDecimal)
|
|
125
|
-
.option('--id [offer-id]', 'Offer ID')
|
|
126
|
-
.option('--fee [fee]', 'Cosmos fee', parseFee)
|
|
127
|
-
.action(() => {
|
|
128
|
-
console.error('TODO actually send withdrawal');
|
|
129
|
-
// TODO: Implement withdraw logic
|
|
130
|
-
});
|
|
82
|
+
addLPCommands(program, { fetch, stdout, stderr, env, now });
|
|
131
83
|
|
|
132
84
|
program
|
|
133
85
|
.command('transfer')
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import {Command} from 'commander';
|
|
3
|
+
* @import {OfferSpec} from '@agoric/smart-wallet/src/offers.js';
|
|
4
|
+
* @import {ExecuteOfferAction} from '@agoric/smart-wallet/src/smartWallet.js';
|
|
5
|
+
* @import {USDCProposalShapes} from '../pool-share-math.js';
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { fetchEnvNetworkConfig, makeVstorageKit } from '@agoric/client-utils';
|
|
9
|
+
import { InvalidArgumentError } from 'commander';
|
|
10
|
+
import {
|
|
11
|
+
assertParsableNumber,
|
|
12
|
+
ceilDivideBy,
|
|
13
|
+
multiplyBy,
|
|
14
|
+
parseRatio,
|
|
15
|
+
} from '@agoric/zoe/src/contractSupport/ratio.js';
|
|
16
|
+
import { AmountMath } from '@agoric/ertp';
|
|
17
|
+
import { outputActionAndHint } from './bridge-action.js';
|
|
18
|
+
|
|
19
|
+
/** @param {string} arg */
|
|
20
|
+
const parseDecimal = arg => {
|
|
21
|
+
try {
|
|
22
|
+
assertParsableNumber(arg);
|
|
23
|
+
const n = Number(arg);
|
|
24
|
+
return n;
|
|
25
|
+
} catch {
|
|
26
|
+
throw new InvalidArgumentError('Not a number');
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {string} amountString
|
|
32
|
+
* @param {Brand} usdc
|
|
33
|
+
*/
|
|
34
|
+
const parseUSDCAmount = (amountString, usdc) => {
|
|
35
|
+
const USDC_DECIMALS = 6;
|
|
36
|
+
const unit = AmountMath.make(usdc, 10n ** BigInt(USDC_DECIMALS));
|
|
37
|
+
return multiplyBy(unit, parseRatio(amountString, usdc));
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {Command} program
|
|
42
|
+
* @param {{
|
|
43
|
+
* fetch?: Window['fetch'];
|
|
44
|
+
* vstorageKit?: Awaited<ReturnType<typeof makeVstorageKit>>;
|
|
45
|
+
* stdout: typeof process.stdout;
|
|
46
|
+
* stderr: typeof process.stderr;
|
|
47
|
+
* env: typeof process.env;
|
|
48
|
+
* now: typeof Date.now;
|
|
49
|
+
* }} io
|
|
50
|
+
*/
|
|
51
|
+
export const addLPCommands = (
|
|
52
|
+
program,
|
|
53
|
+
{ fetch, vstorageKit, stderr, stdout, env, now },
|
|
54
|
+
) => {
|
|
55
|
+
const loadVsk = async () => {
|
|
56
|
+
if (vstorageKit) {
|
|
57
|
+
return vstorageKit;
|
|
58
|
+
}
|
|
59
|
+
assert(fetch);
|
|
60
|
+
const networkConfig = await fetchEnvNetworkConfig({ env, fetch });
|
|
61
|
+
return makeVstorageKit({ fetch }, networkConfig);
|
|
62
|
+
};
|
|
63
|
+
/** @type {undefined | ReturnType<typeof loadVsk>} */
|
|
64
|
+
let vskP;
|
|
65
|
+
|
|
66
|
+
program
|
|
67
|
+
.command('deposit')
|
|
68
|
+
.description('Deposit USDC into pool')
|
|
69
|
+
.addHelpText(
|
|
70
|
+
'after',
|
|
71
|
+
'\nPipe the STDOUT to a file such as deposit.json, then use the Agoric CLI to broadcast it:\n agoric wallet send --offer deposit.json --from gov1 --keyring-backend="test"',
|
|
72
|
+
)
|
|
73
|
+
.requiredOption('--amount <number>', 'USDC amount', parseDecimal)
|
|
74
|
+
.option('--offerId <string>', 'Offer id', String, `lpDeposit-${now()}`)
|
|
75
|
+
.action(async opts => {
|
|
76
|
+
vskP ||= loadVsk();
|
|
77
|
+
const vsk = await vskP;
|
|
78
|
+
/** @type {Brand<'nat'>} */
|
|
79
|
+
// @ts-expect-error it doesnt recognize usdc as a Brand type
|
|
80
|
+
const usdc = vsk.agoricNames.brand.USDC;
|
|
81
|
+
assert(usdc, 'USDC brand not in agoricNames');
|
|
82
|
+
|
|
83
|
+
const usdcAmount = parseUSDCAmount(opts.amount, usdc);
|
|
84
|
+
|
|
85
|
+
/** @type {USDCProposalShapes['deposit']} */
|
|
86
|
+
const proposal = {
|
|
87
|
+
give: {
|
|
88
|
+
USDC: usdcAmount,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/** @type {OfferSpec} */
|
|
93
|
+
const offer = {
|
|
94
|
+
id: opts.offerId,
|
|
95
|
+
invitationSpec: {
|
|
96
|
+
source: 'agoricContract',
|
|
97
|
+
instancePath: ['fastUsdc'],
|
|
98
|
+
callPipe: [['makeDepositInvitation', []]],
|
|
99
|
+
},
|
|
100
|
+
proposal,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/** @type {ExecuteOfferAction} */
|
|
104
|
+
const bridgeAction = {
|
|
105
|
+
method: 'executeOffer',
|
|
106
|
+
offer,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
outputActionAndHint(bridgeAction, { stderr, stdout }, vsk.marshaller);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
program
|
|
113
|
+
.command('withdraw')
|
|
114
|
+
.description("Withdraw USDC from the LP's pool share")
|
|
115
|
+
.addHelpText(
|
|
116
|
+
'after',
|
|
117
|
+
'\nPipe the STDOUT to a file such as withdraw.json, then use the Agoric CLI to broadcast it:\n agoric wallet send --offer withdraw.json --from gov1 --keyring-backend="test"',
|
|
118
|
+
)
|
|
119
|
+
.requiredOption('--amount <number>', 'USDC amount', parseDecimal)
|
|
120
|
+
.option('--offerId <string>', 'Offer id', String, `lpWithdraw-${now()}`)
|
|
121
|
+
.action(async opts => {
|
|
122
|
+
vskP ||= loadVsk();
|
|
123
|
+
const vsk = await vskP;
|
|
124
|
+
|
|
125
|
+
/** @type {Brand<'nat'>} */
|
|
126
|
+
// @ts-expect-error it doesnt recognize FastLP as a Brand type
|
|
127
|
+
const poolShare = vsk.agoricNames.brand.FastLP;
|
|
128
|
+
assert(poolShare, 'FastLP brand not in agoricNames');
|
|
129
|
+
|
|
130
|
+
/** @type {Brand<'nat'>} */
|
|
131
|
+
// @ts-expect-error it doesnt recognize usdc as a Brand type
|
|
132
|
+
const usdc = vsk.agoricNames.brand.USDC;
|
|
133
|
+
assert(usdc, 'USDC brand not in agoricNames');
|
|
134
|
+
|
|
135
|
+
const usdcAmount = parseUSDCAmount(opts.amount, usdc);
|
|
136
|
+
|
|
137
|
+
/** @type {import('../types.js').PoolMetrics} */
|
|
138
|
+
// @ts-expect-error it treats this as "unknown"
|
|
139
|
+
const metrics = await vsk.readPublished('fastUsdc.poolMetrics');
|
|
140
|
+
const fastLPAmount = ceilDivideBy(usdcAmount, metrics.shareWorth);
|
|
141
|
+
|
|
142
|
+
/** @type {USDCProposalShapes['withdraw']} */
|
|
143
|
+
const proposal = {
|
|
144
|
+
give: {
|
|
145
|
+
PoolShare: fastLPAmount,
|
|
146
|
+
},
|
|
147
|
+
want: {
|
|
148
|
+
USDC: usdcAmount,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/** @type {OfferSpec} */
|
|
153
|
+
const offer = {
|
|
154
|
+
id: opts.offerId,
|
|
155
|
+
invitationSpec: {
|
|
156
|
+
source: 'agoricContract',
|
|
157
|
+
instancePath: ['fastUsdc'],
|
|
158
|
+
callPipe: [['makeWithdrawInvitation', []]],
|
|
159
|
+
},
|
|
160
|
+
proposal,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
outputActionAndHint(
|
|
164
|
+
{ method: 'executeOffer', offer },
|
|
165
|
+
{ stderr, stdout },
|
|
166
|
+
vsk.marshaller,
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
return program;
|
|
171
|
+
};
|
|
@@ -80,6 +80,10 @@ export const addOperatorCommands = (
|
|
|
80
80
|
operator
|
|
81
81
|
.command('attest')
|
|
82
82
|
.description('Attest to an observed Fast USDC transfer')
|
|
83
|
+
.addHelpText(
|
|
84
|
+
'after',
|
|
85
|
+
'\nPipe the STDOUT to a file such as attest.json, then use the Agoric CLI to broadcast it:\n agoric wallet send --offer attest.json --from gov1 --keyring-backend="test"',
|
|
86
|
+
)
|
|
83
87
|
.requiredOption('--previousOfferId <string>', 'Offer id', String)
|
|
84
88
|
.requiredOption('--forwardingChannel <string>', 'Channel id', String)
|
|
85
89
|
.requiredOption('--recipientAddress <string>', 'bech32 address', String)
|
|
@@ -13,21 +13,29 @@ import { PendingTxStatus, TxStatus } from '../constants.js';
|
|
|
13
13
|
/**
|
|
14
14
|
* @import {MapStore, SetStore} from '@agoric/store';
|
|
15
15
|
* @import {Zone} from '@agoric/zone';
|
|
16
|
-
* @import {CctpTxEvidence, NobleAddress,
|
|
16
|
+
* @import {CctpTxEvidence, NobleAddress, PendingTx, EvmHash, LogFn} from '../types.js';
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {`pendingTx:${bigint}:${NobleAddress}`} PendingTxKey
|
|
21
|
+
* The string template is for developer visibility but not meant to ever be parsed.
|
|
22
|
+
*
|
|
23
|
+
* @typedef {`seenTx:${string}:${EvmHash}`} SeenTxKey
|
|
24
|
+
* The string template is for developer visibility but not meant to ever be parsed.
|
|
17
25
|
*/
|
|
18
26
|
|
|
19
27
|
/**
|
|
20
28
|
* Create the key for the pendingTxs MapStore.
|
|
21
29
|
*
|
|
22
|
-
* The key is a composite
|
|
23
|
-
* parsable.
|
|
30
|
+
* The key is a composite but not meant to be parsable.
|
|
24
31
|
*
|
|
25
32
|
* @param {NobleAddress} addr
|
|
26
33
|
* @param {bigint} amount
|
|
27
34
|
* @returns {PendingTxKey}
|
|
28
35
|
*/
|
|
29
36
|
const makePendingTxKey = (addr, amount) =>
|
|
30
|
-
|
|
37
|
+
// amount can't contain colon
|
|
38
|
+
`pendingTx:${amount}:${addr}`;
|
|
31
39
|
|
|
32
40
|
/**
|
|
33
41
|
* Get the key for the pendingTxs MapStore.
|
|
@@ -43,15 +51,15 @@ const pendingTxKeyOf = evidence => {
|
|
|
43
51
|
/**
|
|
44
52
|
* Get the key for the seenTxs SetStore.
|
|
45
53
|
*
|
|
46
|
-
* The key is a composite
|
|
47
|
-
* meant to be parsable.
|
|
54
|
+
* The key is a composite but not meant to be parsable.
|
|
48
55
|
*
|
|
49
56
|
* @param {CctpTxEvidence} evidence
|
|
50
57
|
* @returns {SeenTxKey}
|
|
51
58
|
*/
|
|
52
59
|
const seenTxKeyOf = evidence => {
|
|
53
60
|
const { txHash, chainId } = evidence;
|
|
54
|
-
|
|
61
|
+
// chainId can't contain colon
|
|
62
|
+
return `seenTx:${chainId}:${txHash}`;
|
|
55
63
|
};
|
|
56
64
|
|
|
57
65
|
/**
|
|
@@ -68,12 +76,12 @@ const seenTxKeyOf = evidence => {
|
|
|
68
76
|
* XXX consider separate facets for `Advancing` and `Settling` capabilities.
|
|
69
77
|
*
|
|
70
78
|
* @param {Zone} zone
|
|
71
|
-
* @param {
|
|
79
|
+
* @param {ERef<StorageNode>} transactionsNode
|
|
72
80
|
* @param {StatusManagerPowers} caps
|
|
73
81
|
*/
|
|
74
82
|
export const prepareStatusManager = (
|
|
75
83
|
zone,
|
|
76
|
-
|
|
84
|
+
transactionsNode,
|
|
77
85
|
{
|
|
78
86
|
log = makeTracer('Advancer', true),
|
|
79
87
|
} = /** @type {StatusManagerPowers} */ ({}),
|
|
@@ -93,9 +101,8 @@ export const prepareStatusManager = (
|
|
|
93
101
|
* @param {CctpTxEvidence['txHash']} hash
|
|
94
102
|
* @param {TxStatus} status
|
|
95
103
|
*/
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
const txnNodeP = E(statusNodeP).makeChildNode(hash);
|
|
104
|
+
const publishStatus = (hash, status) => {
|
|
105
|
+
const txnNodeP = E(transactionsNode).makeChildNode(hash);
|
|
99
106
|
// Don't await, just writing to vstorage.
|
|
100
107
|
void E(txnNodeP).setValue(status);
|
|
101
108
|
};
|
|
@@ -109,7 +116,7 @@ export const prepareStatusManager = (
|
|
|
109
116
|
* @param {CctpTxEvidence} evidence
|
|
110
117
|
* @param {PendingTxStatus} status
|
|
111
118
|
*/
|
|
112
|
-
const
|
|
119
|
+
const initPendingTx = (evidence, status) => {
|
|
113
120
|
const seenKey = seenTxKeyOf(evidence);
|
|
114
121
|
if (seenTxs.has(seenKey)) {
|
|
115
122
|
throw makeError(`Transaction already seen: ${q(seenKey)}`);
|
|
@@ -121,9 +128,31 @@ export const prepareStatusManager = (
|
|
|
121
128
|
pendingTxKeyOf(evidence),
|
|
122
129
|
harden({ ...evidence, status }),
|
|
123
130
|
);
|
|
124
|
-
|
|
131
|
+
publishStatus(evidence.txHash, status);
|
|
125
132
|
};
|
|
126
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Update the pending transaction status.
|
|
136
|
+
*
|
|
137
|
+
* @param {{sender: NobleAddress, amount: bigint}} keyParts
|
|
138
|
+
* @param {PendingTxStatus} status
|
|
139
|
+
*/
|
|
140
|
+
function setPendingTxStatus({ sender, amount }, status) {
|
|
141
|
+
const key = makePendingTxKey(sender, amount);
|
|
142
|
+
pendingTxs.has(key) || Fail`no advancing tx with ${{ sender, amount }}`;
|
|
143
|
+
const pending = pendingTxs.get(key);
|
|
144
|
+
const ix = pending.findIndex(tx => tx.status === PendingTxStatus.Advancing);
|
|
145
|
+
ix >= 0 || Fail`no advancing tx with ${{ sender, amount }}`;
|
|
146
|
+
const [prefix, tx, suffix] = [
|
|
147
|
+
pending.slice(0, ix),
|
|
148
|
+
pending[ix],
|
|
149
|
+
pending.slice(ix + 1),
|
|
150
|
+
];
|
|
151
|
+
const txpost = { ...tx, status };
|
|
152
|
+
pendingTxs.set(key, harden([...prefix, txpost, ...suffix]));
|
|
153
|
+
publishStatus(tx.txHash, status);
|
|
154
|
+
}
|
|
155
|
+
|
|
127
156
|
return zone.exo(
|
|
128
157
|
'Fast USDC Status Manager',
|
|
129
158
|
M.interface('StatusManagerI', {
|
|
@@ -156,10 +185,13 @@ export const prepareStatusManager = (
|
|
|
156
185
|
{
|
|
157
186
|
/**
|
|
158
187
|
* Add a new transaction with ADVANCING status
|
|
188
|
+
*
|
|
189
|
+
* NB: this acts like observe() but skips recording the OBSERVED state
|
|
190
|
+
*
|
|
159
191
|
* @param {CctpTxEvidence} evidence
|
|
160
192
|
*/
|
|
161
193
|
advance(evidence) {
|
|
162
|
-
|
|
194
|
+
initPendingTx(evidence, PendingTxStatus.Advancing);
|
|
163
195
|
},
|
|
164
196
|
|
|
165
197
|
/**
|
|
@@ -171,24 +203,10 @@ export const prepareStatusManager = (
|
|
|
171
203
|
* @throws {Error} if nothing to advance
|
|
172
204
|
*/
|
|
173
205
|
advanceOutcome(sender, amount, success) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const ix = pending.findIndex(
|
|
178
|
-
tx => tx.status === PendingTxStatus.Advancing,
|
|
206
|
+
setPendingTxStatus(
|
|
207
|
+
{ sender, amount },
|
|
208
|
+
success ? PendingTxStatus.Advanced : PendingTxStatus.AdvanceFailed,
|
|
179
209
|
);
|
|
180
|
-
ix >= 0 || Fail`no advancing tx with ${{ sender, amount }}`;
|
|
181
|
-
const [prefix, tx, suffix] = [
|
|
182
|
-
pending.slice(0, ix),
|
|
183
|
-
pending[ix],
|
|
184
|
-
pending.slice(ix + 1),
|
|
185
|
-
];
|
|
186
|
-
const status = success
|
|
187
|
-
? PendingTxStatus.Advanced
|
|
188
|
-
: PendingTxStatus.AdvanceFailed;
|
|
189
|
-
const txpost = { ...tx, status };
|
|
190
|
-
pendingTxs.set(key, harden([...prefix, txpost, ...suffix]));
|
|
191
|
-
recordStatus(tx.txHash, status);
|
|
192
210
|
},
|
|
193
211
|
|
|
194
212
|
/**
|
|
@@ -196,7 +214,7 @@ export const prepareStatusManager = (
|
|
|
196
214
|
* @param {CctpTxEvidence} evidence
|
|
197
215
|
*/
|
|
198
216
|
observe(evidence) {
|
|
199
|
-
|
|
217
|
+
initPendingTx(evidence, PendingTxStatus.Observed);
|
|
200
218
|
},
|
|
201
219
|
|
|
202
220
|
/**
|
|
@@ -247,7 +265,7 @@ export const prepareStatusManager = (
|
|
|
247
265
|
* @param {EvmHash} txHash
|
|
248
266
|
*/
|
|
249
267
|
disbursed(txHash) {
|
|
250
|
-
|
|
268
|
+
publishStatus(txHash, TxStatus.Disbursed);
|
|
251
269
|
},
|
|
252
270
|
|
|
253
271
|
/**
|
|
@@ -259,7 +277,7 @@ export const prepareStatusManager = (
|
|
|
259
277
|
*/
|
|
260
278
|
forwarded(txHash, address, amount) {
|
|
261
279
|
if (txHash) {
|
|
262
|
-
|
|
280
|
+
publishStatus(txHash, TxStatus.Forwarded);
|
|
263
281
|
} else {
|
|
264
282
|
// TODO store (early) `Minted` transactions to check against incoming evidence
|
|
265
283
|
log(
|
|
@@ -95,8 +95,8 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
|
|
|
95
95
|
marshaller,
|
|
96
96
|
);
|
|
97
97
|
|
|
98
|
-
const
|
|
99
|
-
const statusManager = prepareStatusManager(zone,
|
|
98
|
+
const statusNode = E(storageNode).makeChildNode(STATUS_NODE);
|
|
99
|
+
const statusManager = prepareStatusManager(zone, statusNode);
|
|
100
100
|
|
|
101
101
|
const { USDC } = terms.brands;
|
|
102
102
|
const { withdrawToSeat } = tools.zoeTools;
|
package/src/types.ts
CHANGED
|
@@ -38,12 +38,6 @@ export interface PendingTx extends CctpTxEvidence {
|
|
|
38
38
|
status: PendingTxStatus;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
/** internal key for `StatusManager` exo */
|
|
42
|
-
export type PendingTxKey = `pendingTx:${string}`;
|
|
43
|
-
|
|
44
|
-
/** internal key for `StatusManager` exo */
|
|
45
|
-
export type SeenTxKey = `seenTx:${string}`;
|
|
46
|
-
|
|
47
41
|
export type FeeConfig = {
|
|
48
42
|
flat: Amount<'nat'>;
|
|
49
43
|
variableRate: Ratio;
|
package/src/util/agoric.js
CHANGED
|
@@ -5,7 +5,7 @@ export const queryFastUSDCLocalChainAccount = async (
|
|
|
5
5
|
out = console,
|
|
6
6
|
) => {
|
|
7
7
|
const agoricAddr = await vstorage.readLatest(
|
|
8
|
-
'published.
|
|
8
|
+
'published.fastUsdc.settlementAccount',
|
|
9
9
|
);
|
|
10
10
|
out.log(`Got Fast USDC Local Chain Account ${agoricAddr}`);
|
|
11
11
|
return agoricAddr;
|