@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.
- package/README.md +43 -0
- package/package.json +29 -25
- package/src/add-operators.core.js +63 -0
- package/src/cli/bridge-action.js +40 -0
- package/src/cli/cli.js +46 -153
- package/src/cli/config-commands.js +108 -0
- package/src/cli/config.js +15 -9
- package/src/cli/lp-commands.js +160 -0
- package/src/cli/operator-commands.js +146 -0
- package/src/cli/transfer.js +63 -13
- package/src/{util → cli/util}/agoric.js +1 -1
- package/src/cli/util/bank.js +12 -0
- package/src/{util → cli/util}/cctp.js +1 -1
- package/src/{util → cli/util}/file.js +1 -1
- package/src/clientSupport.js +98 -0
- package/src/constants.js +25 -2
- package/src/distribute-fees.core.js +93 -0
- package/src/exos/advancer.js +220 -106
- package/src/exos/liquidity-pool.js +130 -81
- package/src/exos/operator-kit.js +16 -12
- package/src/exos/settler.js +360 -64
- package/src/exos/status-manager.js +316 -65
- package/src/exos/transaction-feed.js +121 -42
- package/src/fast-usdc-policy.core.js +65 -0
- package/src/fast-usdc.contract.js +165 -84
- package/src/fast-usdc.flows.js +10 -0
- package/src/main.js +1 -0
- package/src/pool-share-math.js +55 -9
- package/src/{fast-usdc.start.js → start-fast-usdc.core.js} +48 -86
- package/src/type-guards.js +75 -24
- package/src/types.ts +89 -14
- package/src/utils/chain-policies.js +140 -0
- package/src/utils/core-eval.js +73 -0
- package/src/utils/deploy-config.js +127 -0
- package/src/utils/fees.js +3 -4
- package/tools/cli-tools.ts +9 -0
- package/tools/mock-io.ts +14 -0
- package/src/exos/README.md +0 -26
- package/src/utils/address.js +0 -71
- /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,
|
|
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
|
|
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
|
|
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
|
+
};
|
package/tools/mock-io.ts
ADDED
|
@@ -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;
|
package/src/exos/README.md
DELETED
|
@@ -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
|
-
```
|
package/src/utils/address.js
DELETED
|
@@ -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
|