Skip to content

Commit 302c5d5

Browse files
committed
chore: format both kinds of bids
1 parent c58076a commit 302c5d5

File tree

3 files changed

+118
-57
lines changed

3 files changed

+118
-57
lines changed

packages/agoric-cli/src/inter.js

+44-20
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import '@endo/init';
33
import { iterateLatest, makeFollower, makeLeader } from '@agoric/casting';
44
import { M, matches } from '@agoric/store';
55
import { Offers } from '@agoric/inter-protocol/src/clientSupport.js';
6+
import { objectMap } from '@agoric/internal';
67
import {
78
boardSlottingMarshaller,
89
getNetworkConfig,
@@ -12,49 +13,72 @@ import { coalesceWalletState, outputExecuteOfferAction } from './lib/wallet.js';
1213
import { normalizeAddressWithOptions } from './lib/chain.js';
1314
import { makeAmountFormatter } from './lib/format.js';
1415

15-
const { entries, fromEntries, values } = Object;
16+
const { values } = Object;
1617

17-
const mapValues = (obj, fn) =>
18-
fromEntries(entries(obj).map(([prop, val]) => [prop, fn(val)]));
18+
const makeFormatters = assets => {
19+
const fmtAmtTuple = makeAmountFormatter(assets);
20+
/** @param {Amount} amt */
21+
const amount = amt => (([l, m]) => `${m}${l}`)(fmtAmtTuple(amt));
22+
/** @param {Record<string, Amount> | undefined} r */
23+
const record = r => (r ? objectMap(r, amount) : undefined);
24+
/** @param {Ratio} r */
25+
const price = r => {
26+
const [nl, nm] = fmtAmtTuple(r.numerator);
27+
const [dl, dm] = fmtAmtTuple(r.denominator);
28+
return `${Number(nm) / Number(dm)} ${nl}/${dl}`;
29+
};
30+
const discount = r =>
31+
100 - (Number(r.numerator.value) / Number(r.denominator.value)) * 100;
32+
return { amount, record, price, discount };
33+
};
1934

2035
const fmtMetrics = (metrics, quote, assets) => {
21-
const fmtAmtTuple = makeAmountFormatter(assets);
22-
const fmtAmt = amt => (([l, m]) => `${m}${l}`)(fmtAmtTuple(amt));
36+
const fmt = makeFormatters(assets);
2337
const { liquidatingCollateral, liquidatingDebt } = metrics;
2438

2539
const {
2640
quoteAmount: {
2741
value: [{ amountIn, amountOut }],
2842
},
2943
} = quote;
30-
// TODO: divide num, denom by GCD
31-
const price = `${fmtAmt(amountOut)}/${fmtAmt(amountIn)}}`;
44+
const price = fmt.price({ numerator: amountOut, denominator: amountIn });
3245

33-
const amounts = mapValues({ liquidatingCollateral, liquidatingDebt }, fmtAmt);
46+
const amounts = objectMap(
47+
{ liquidatingCollateral, liquidatingDebt },
48+
fmt.amount,
49+
);
3450
return { ...amounts, price };
3551
};
3652

53+
/**
54+
* @param {import('@agoric/smart-wallet/src/offers.js').OfferStatus &
55+
* { offerArgs: import('@agoric/inter-protocol/src/auction/auctionBook.js').BidSpec}} bid
56+
* @param {import('./lib/format.js').AssetDescriptor[]} assets
57+
*/
3758
export const fmtBid = (bid, assets) => {
38-
const fmtAmtTuple = makeAmountFormatter(assets);
39-
const fmtAmt = amt => (([l, m]) => `${m}${l}`)(fmtAmtTuple(amt));
40-
const fmtRecord = r => (r ? mapValues(r, fmtAmt) : undefined);
59+
const fmt = makeFormatters(assets);
60+
61+
const { offerArgs } = bid;
62+
/** @type {{ price: string } | { discount: number }} */
63+
const spec =
64+
'offerPrice' in offerArgs
65+
? { price: fmt.price(offerArgs.offerPrice) }
66+
: { discount: fmt.discount(offerArgs.offerBidScaling) };
4167

4268
const {
4369
id,
4470
error,
4571
proposal: { give },
46-
offerArgs: { want, offerPrice }, // TODO: other kind of bid
72+
offerArgs: { want },
4773
payouts,
4874
} = bid;
49-
const amounts = {
50-
give: give ? fmtRecord(give) : undefined,
51-
want: want ? fmtAmt(want) : undefined,
52-
price: offerPrice
53-
? `${fmtAmt(offerPrice.numerator)}/${fmtAmt(offerPrice.denominator)}}`
54-
: undefined,
55-
payouts: fmtRecord(payouts),
75+
const props = {
76+
...(give ? { give: fmt.record(give) } : {}),
77+
...(want ? { want: fmt.amount(want) } : {}),
78+
...(payouts ? { payouts: fmt.record(payouts) } : {}),
79+
...(error ? { error } : {}),
5680
};
57-
return harden({ id, ...amounts, error });
81+
return harden({ id, ...spec, ...props });
5882
};
5983

6084
/** distinguish IO errors etc. from logic bugs */

packages/agoric-cli/src/lib/format.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,18 @@ const exampleAsset = {
3030
issuer: makeBoardRemote({ boardId: null, iface: undefined }),
3131
petname: 'Agoric staking token',
3232
};
33-
/** @typedef {import('@agoric/vats/tools/board-utils.js').VBankAssetDetail & { brand: import('@agoric/vats/tools/board-utils.js').BoardRemote }} AssetDescriptor */
33+
/** @typedef {import('@agoric/vats/tools/board-utils.js').VBankAssetDetail } AssetDescriptor */
3434

35-
/** @param {AssetDescriptor[]} assets */
35+
/**
36+
* @param {AssetDescriptor[]} assets
37+
* @returns {(a: Amount) => [string, number | any[]]}
38+
*/
3639
export const makeAmountFormatter = assets => amt => {
3740
const { brand, value } = amt;
41+
// @ts-expect-error XXX BoardRemote
3842
const boardId = brand.getBoardId();
3943
const asset = assets.find(a => a.brand.getBoardId() === boardId);
40-
if (!asset) return [NaN, boardId];
44+
if (!asset) return ['?', boardId];
4145
const {
4246
displayInfo: { assetKind, decimalPlaces = 0 },
4347
issuerName,
@@ -46,12 +50,13 @@ export const makeAmountFormatter = assets => amt => {
4650
case 'nat':
4751
return [issuerName, Number(value) / 10 ** decimalPlaces];
4852
case 'set':
53+
assert(Array.isArray(value));
4954
if (value[0]?.handle?.iface?.includes('InvitationHandle')) {
5055
return [issuerName, value.map(v => v.description)];
5156
}
5257
return [issuerName, value];
5358
default:
54-
return [issuerName, ['?']];
59+
return [issuerName, [NaN]];
5560
}
5661
};
5762

packages/agoric-cli/test/test-inter.js

+65-33
Original file line numberDiff line numberDiff line change
@@ -3,77 +3,109 @@ import test from 'ava';
33
import { fmtBid } from '../src/inter.js';
44

55
const brand = {
6-
IbcATOM: { getBoardId: () => 'board00848' },
6+
ATOM: { getBoardId: () => 'board00848' },
77
IST: { getBoardId: () => 'board0566' },
88
};
99

10-
const agoricNames = {
10+
const agoricNames = harden({
1111
brand,
1212

1313
vbankAsset: {
1414
uist: {
1515
brand: brand.IST,
1616
denom: 'uist',
17-
displayInfo: {
18-
assetKind: 'nat',
19-
decimalPlaces: 6,
20-
},
17+
displayInfo: { assetKind: 'nat', decimalPlaces: 6 },
2118
issuer: {},
2219
issuerName: 'IST',
2320
proposedName: 'Agoric stable local currency',
2421
},
2522

2623
'ibc/toyatom': {
27-
brand: brand.IbcATOM,
24+
brand: brand.ATOM,
2825
denom: 'ibc/toyatom',
29-
displayInfo: {
30-
assetKind: 'nat',
31-
decimalPlaces: 6,
32-
},
26+
displayInfo: { assetKind: 'nat', decimalPlaces: 6 },
3327
issuer: {},
34-
issuerName: 'IbcATOM',
28+
issuerName: 'ATOM',
3529
proposedName: 'ATOM',
3630
},
3731
},
38-
};
32+
});
3933

40-
const offerStatus1 = {
34+
/**
35+
* @type {import('@agoric/smart-wallet/src/offers.js').OfferStatus &
36+
* { offerArgs: import('@agoric/inter-protocol/src/auction/auctionBook.js').BidSpec}}
37+
*/
38+
const offerStatus1 = harden({
4139
error: 'Error: "nameKey" not found: (a string)',
4240
id: 1678990150266,
4341
invitationSpec: {
44-
callPipe: [['getBidInvitation', [brand.IbcATOM]]],
42+
callPipe: [['getBidInvitation', [brand.ATOM]]],
4543
instancePath: ['auctioneer'],
4644
source: 'agoricContract',
4745
},
4846
offerArgs: {
4947
offerPrice: {
50-
denominator: { brand: brand.IbcATOM, value: 2000000n },
48+
denominator: { brand: brand.ATOM, value: 2000000n },
5149
numerator: { brand: brand.IST, value: 20000000n },
5250
},
53-
want: {
54-
Collateral: { brand: brand.IbcATOM, value: 2000000n },
51+
want: { brand: brand.ATOM, value: 2000000n },
52+
},
53+
proposal: {
54+
give: {
55+
Currency: { brand: brand.ATOM, value: 20000000n },
56+
},
57+
},
58+
});
59+
60+
/**
61+
* @type {import('@agoric/smart-wallet/src/offers.js').OfferStatus &
62+
* { offerArgs: import('@agoric/inter-protocol/src/auction/auctionBook.js').BidSpec}}
63+
*/
64+
const offerStatus2 = harden({
65+
id: 'bid-234234',
66+
invitationSpec: {
67+
callPipe: [['getBidInvitation', [brand.ATOM]]],
68+
instancePath: ['auctioneer'],
69+
source: 'agoricContract',
70+
},
71+
offerArgs: {
72+
offerBidScaling: {
73+
denominator: { brand: brand.IST, value: 100n },
74+
numerator: { brand: brand.IST, value: 90n },
5575
},
76+
want: { brand: brand.ATOM, value: 2000000n },
5677
},
5778
proposal: {
5879
give: {
59-
Currency: { brand: brand.IbcATOM, value: 20000000n },
80+
Currency: { brand: brand.ATOM, value: 20000000n },
6081
},
6182
},
62-
};
83+
payouts: {
84+
Collateral: { brand: brand.ATOM, value: 5_000_000n },
85+
Currency: { brand: brand.IST, value: 37_000_000n },
86+
},
87+
});
6388

6489
test('formatBid', t => {
6590
const { values } = Object;
66-
const actual = fmtBid(offerStatus1, values(agoricNames.vbankAsset));
67-
t.deepEqual(actual, {
68-
id: 1678990150266,
69-
error: 'Error: "nameKey" not found: (a string)',
70-
give: {
71-
Currency: '20IbcATOM',
72-
},
73-
payouts: undefined,
74-
price: '20IST/2IbcATOM}',
75-
want: {
76-
Collateral: '2IbcATOM',
77-
},
78-
});
91+
{
92+
const actual = fmtBid(offerStatus1, values(agoricNames.vbankAsset));
93+
t.deepEqual(actual, {
94+
id: 1678990150266,
95+
error: 'Error: "nameKey" not found: (a string)',
96+
give: { Currency: '20ATOM' },
97+
price: '10 IST/ATOM',
98+
want: '2ATOM',
99+
});
100+
}
101+
{
102+
const actual = fmtBid(offerStatus2, values(agoricNames.vbankAsset));
103+
t.deepEqual(actual, {
104+
id: 'bid-234234',
105+
give: { Currency: '20ATOM' },
106+
payouts: { Collateral: '5ATOM', Currency: '37IST' },
107+
want: '2ATOM',
108+
discount: 10,
109+
});
110+
}
79111
});

0 commit comments

Comments
 (0)