Skip to content

Commit 2422c58

Browse files
authored
refactorings to support AccountId pervasively (#11223)
_incidental_ ## Description The move from `CosmosChainAddress` to CAIP-10 `AccountId` has been incremental. This smooths out one of the common cases where a function needs to pass along a `CosmosChainAddress` but expanding the `makeChainAddress` function to coerce its AccountIdArg into a CosmosChainAddress. It renames it to `coerceCosmosAddress` to make clear its a coercer and that it's only for Cosmos accounts. It also does a bunch of type narrowing to make the distinctions clearer. ### Security Considerations none ### Scaling Considerations none ### Documentation Considerations none ### Testing Considerations CI ### Upgrade Considerations This renames a method on ChainHub. That's safe because there's a new one made in each incarnation.
2 parents afbbd9a + a5b7a49 commit 2422c58

29 files changed

+174
-141
lines changed

multichain-testing/test/fast-usdc/noble-forwarding.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import anyTest from '@endo/ses-ava/prepare-endo.js';
22
import type { TestFn } from 'ava';
33
import { commonSetup, type SetupContext } from '../support.js';
44
import { createWallet } from '../../tools/wallet.js';
5-
import type { IBCConnectionInfo } from '@agoric/orchestration';
5+
import type { Bech32Address, IBCConnectionInfo } from '@agoric/orchestration';
66
import { makeQueryClient } from '../../tools/query.js';
77

88
const test = anyTest as TestFn<SetupContext>;
@@ -12,7 +12,8 @@ test('noble forwarding', async t => {
1212
await commonSetup(t, { config: '../config.fusdc.yaml' });
1313

1414
const agoricWallet = await createWallet('agoric');
15-
const agoricAddr = (await agoricWallet.getAccounts())[0].address;
15+
const agoricAddr = (await agoricWallet.getAccounts())[0]
16+
.address as Bech32Address;
1617
t.log('Made agoric wallet:', agoricAddr);
1718

1819
const agoricChainId = useChain('agoric').chain.chain_id;

packages/boot/tools/drivers.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable jsdoc/require-param */
21
import { Fail } from '@endo/errors';
32
import { type Amount } from '@agoric/ertp';
43
import { NonNullish } from '@agoric/internal';

packages/cosmic-proto/src/address-hooks.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@
3131
import { bech32 } from 'bech32';
3232
import queryString from 'query-string';
3333

34+
/**
35+
* @typedef {`${string}1${string}`} Bech32Address
36+
* @example
37+
*
38+
* agoric1megzytg65cyrgzs6fvzxgrcqvwwl7ugpt62346
39+
* cosmosvaloper1npm9gvss52mlmk
40+
*/
41+
3442
/* global globalThis */
3543
/** @type {<T>(x: T) => T} */
3644
const harden = globalThis.harden || Object.freeze;
@@ -94,15 +102,17 @@ harden(decodeBech32);
94102
* @param {string} humanReadablePart
95103
* @param {ArrayLike<number>} bytes
96104
* @param {number} [charLimit]
97-
* @returns {string}
105+
* @returns {Bech32Address}
98106
*/
99107
export const encodeBech32 = (
100108
humanReadablePart,
101109
bytes,
102110
charLimit = DEFAULT_HOOKED_ADDRESS_CHAR_LIMIT,
103111
) => {
104112
const words = bech32.toWords(bytes);
105-
return bech32.encode(humanReadablePart, words, charLimit);
113+
return /** @type {Bech32Address} */ (
114+
bech32.encode(humanReadablePart, words, charLimit)
115+
);
106116
};
107117
harden(encodeBech32);
108118

@@ -126,7 +136,7 @@ harden(encodeBech32);
126136
* @param {string} baseAddress
127137
* @param {ArrayLike<number>} hookData
128138
* @param {number} [charLimit]
129-
* @returns {string}
139+
* @returns {Bech32Address}
130140
*/
131141
export const joinHookedAddress = (
132142
baseAddress,
@@ -178,6 +188,7 @@ harden(joinHookedAddress);
178188
* @param {string} baseAddress
179189
* @param {HookQuery} query
180190
* @param {number} [charLimit]
191+
* @returns {Bech32Address}
181192
*/
182193
export const encodeAddressHook = (baseAddress, query, charLimit) => {
183194
const queryStr = queryString.stringify(query);

packages/cosmic-proto/test/address-hooks.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ const lengthCheckMacro = test.macro({
228228
* [
229229
* baseAddress: string,
230230
* query: import('../src/address-hooks.js').HookQuery,
231-
* expected: string,
231+
* expected: import('../src/address-hooks.js').Bech32Address,
232232
* ]
233233
* >}
234234
*/

packages/fast-usdc-contract/src/exos/advancer.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import type { ZCF, ZCFSeat } from '@agoric/zoe/src/zoeService/zoe.js';
4545
import type { Zone } from '@agoric/zone';
4646
import { Fail, q } from '@endo/errors';
4747
import { E } from '@endo/far';
48-
import { M, mustMatch } from '@endo/patterns';
48+
import { M, mustMatch } from '@agoric/store';
4949
import type { LiquidityPoolKit } from './liquidity-pool.js';
5050
import type { SettlerKit } from './settler.js';
5151
import type { StatusManager } from './status-manager.js';
@@ -200,7 +200,6 @@ export const prepareAdvancerKit = (
200200
}
201201
const { EUD } = decoded.query;
202202
log(`decoded EUD: ${EUD}`);
203-
assert.typeof(EUD, 'string');
204203
// throws if the bech32 prefix is not found
205204
const destination = chainHub.resolveAccountId(EUD);
206205

packages/fast-usdc-contract/src/exos/settler.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { M } from '@endo/patterns';
1717
import { decodeAddressHook } from '@agoric/cosmic-proto/address-hooks.js';
1818
import { PendingTxStatus } from '@agoric/fast-usdc/src/constants.js';
1919
import {
20+
AddressHookShape,
2021
CctpTxEvidenceShape,
2122
EvmHashShape,
2223
makeNatAmountShape,
@@ -36,14 +37,16 @@ import type {
3637
} from '@agoric/fast-usdc/src/types.js';
3738
import type {
3839
AccountId,
40+
AccountIdArg,
41+
Bech32Address,
3942
ChainHub,
4043
CosmosChainAddress,
4144
Denom,
4245
OrchestrationAccount,
4346
} from '@agoric/orchestration';
4447
import { parseAccountId } from '@agoric/orchestration/src/utils/address.js';
4548
import type { WithdrawToSeat } from '@agoric/orchestration/src/utils/zoe-tools.js';
46-
import type { MapStore } from '@agoric/store';
49+
import { mustMatch, type MapStore } from '@agoric/store';
4750
import type { IBCChannelID, IBCPacket, VTransferIBCEvent } from '@agoric/vats';
4851
import type { TargetRegistration } from '@agoric/vats/src/bridge-target.js';
4952
import type { VowTools } from '@agoric/vow';
@@ -58,7 +61,7 @@ const decodeEventPacket = (
5861
{ data }: IBCPacket,
5962
remoteDenom: string,
6063
):
61-
| { nfa: NobleAddress; amount: bigint; EUD: string }
64+
| { nfa: NobleAddress; amount: bigint; EUD: AccountId | Bech32Address }
6265
| { error: unknown[] } => {
6366
// NB: may not be a FungibleTokenPacketData or even JSON
6467
let tx: FungibleTokenPacketData;
@@ -76,15 +79,12 @@ const decodeEventPacket = (
7679
return { error: ['unexpected denom', { actual, expected: remoteDenom }] };
7780
}
7881

79-
let EUD;
82+
let EUD: Bech32Address;
8083
try {
81-
({ EUD } = decodeAddressHook(tx.receiver).query);
82-
if (!EUD) {
83-
return { error: ['no EUD parameter', tx.receiver] };
84-
}
85-
if (typeof EUD !== 'string') {
86-
return { error: ['EUD is not a string', EUD] };
87-
}
84+
const decoded = decodeAddressHook(tx.receiver);
85+
mustMatch(decoded, AddressHookShape);
86+
87+
({ EUD } = decoded.query);
8888
} catch (e) {
8989
return { error: ['no query params', tx.receiver] };
9090
}
@@ -362,15 +362,11 @@ export const prepareSettler = (
362362
/**
363363
* The intended payee received an advance from the pool. When the funds
364364
* are minted, disburse them to the pool and fee seats.
365-
*
366-
* @param {EvmHash} txHash
367-
* @param {NatValue} fullValue
368-
* @param {AccountId | CosmosChainAddress['value']} EUD
369365
*/
370366
async disburse(
371367
txHash: EvmHash,
372368
fullValue: NatValue,
373-
EUD: AccountId | CosmosChainAddress['value'],
369+
EUD: AccountId | Bech32Address,
374370
) {
375371
const { repayer, settlementAccount } = this.state;
376372
const received = AmountMath.make(USDC, fullValue);
@@ -407,7 +403,11 @@ export const prepareSettler = (
407403
* @param {NatValue} fullValue
408404
* @param {string} EUD
409405
*/
410-
forward(txHash: EvmHash, fullValue: NatValue, EUD: string) {
406+
forward(
407+
txHash: EvmHash,
408+
fullValue: NatValue,
409+
EUD: AccountId | Bech32Address,
410+
) {
411411
const { settlementAccount } = this.state;
412412
log('forwarding', fullValue, 'to', EUD, 'for', txHash);
413413

packages/fast-usdc-contract/test/exos/advancer.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ test('updates status to ADVANCE_SKIPPED on insufficient pool funds', async t =>
330330
]);
331331
});
332332

333-
test('updates status to ADVANCE_SKIPPED if makeChainAddress fails', async t => {
333+
test('updates status to ADVANCE_SKIPPED if coerceCosmosAddress fails', async t => {
334334
const {
335335
bootstrap: { storage },
336336
extensions: {
@@ -352,7 +352,7 @@ test('updates status to ADVANCE_SKIPPED if makeChainAddress fails', async t => {
352352
status: 'ADVANCE_SKIPPED',
353353
},
354354
],
355-
'ADVANCE_SKIPPED status on makeChainAddress failure',
355+
'ADVANCE_SKIPPED status on coerceCosmosAddress failure',
356356
);
357357

358358
t.deepEqual(inspectLogs(), [

packages/fast-usdc-contract/test/exos/settler.test.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,23 @@ import {
55
decodeAddressHook,
66
encodeAddressHook,
77
} from '@agoric/cosmic-proto/address-hooks.js';
8+
import { PendingTxStatus, TxStatus } from '@agoric/fast-usdc/src/constants.js';
9+
import { AddressHookShape } from '@agoric/fast-usdc/src/type-guards.js';
10+
import type { CctpTxEvidence } from '@agoric/fast-usdc/src/types.js';
11+
import { makeFeeTools } from '@agoric/fast-usdc/src/utils/fees.js';
812
import { defaultMarshaller } from '@agoric/internal/src/storage-test-utils.js';
913
import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js';
14+
import cctpChainInfo from '@agoric/orchestration/src/cctp-chain-info.js';
1015
import fetchedChainInfo from '@agoric/orchestration/src/fetched-chain-info.js';
1116
import { buildVTransferEvent } from '@agoric/orchestration/tools/ibc-mocks.js';
12-
import type { Zone } from '@agoric/zone';
13-
import type { EReturn } from '@endo/far';
17+
import { mustMatch } from '@agoric/store';
1418
import type {
1519
AmountKeywordRecord,
1620
TransferPart,
1721
ZcfSeatKit,
1822
} from '@agoric/zoe';
19-
import { PendingTxStatus, TxStatus } from '@agoric/fast-usdc/src/constants.js';
20-
import type { CctpTxEvidence } from '@agoric/fast-usdc/src/types.js';
21-
import { makeFeeTools } from '@agoric/fast-usdc/src/utils/fees.js';
22-
import type { Baggage } from '@agoric/vat-data';
23-
import cctpChainInfo from '@agoric/orchestration/src/cctp-chain-info.js';
23+
import type { Zone } from '@agoric/zone';
24+
import type { EReturn } from '@endo/far';
2425
import {
2526
prepareSettler,
2627
type SettlerKit,
@@ -133,10 +134,9 @@ const makeTestContext = async t => {
133134
const { txHash } = evidence;
134135
const { forwardingAddress, amount } = evidence.tx;
135136
const { recipientAddress } = evidence.aux;
136-
const { EUD } = decodeAddressHook(recipientAddress).query;
137-
if (typeof EUD !== 'string') {
138-
throw Error(`EUD not found in ${recipientAddress}`);
139-
}
137+
const decoded = decodeAddressHook(recipientAddress);
138+
mustMatch(decoded, AddressHookShape);
139+
const { EUD } = decoded.query;
140140
return harden({
141141
txHash,
142142
forwardingAddress,
@@ -875,7 +875,7 @@ test('bad packet data', async t => {
875875
t.deepEqual(inspectLogs().at(-1), [
876876
'invalid event packet',
877877
[
878-
'no EUD parameter',
878+
'no query params',
879879
'agoric10rchps2sfet5lleu7xhs6ztgeehkm5lz5rpkz0cqzs95zdge',
880880
],
881881
]);

packages/fast-usdc-contract/test/fast-usdc.contract.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ import {
2828
type Publisher,
2929
type Subscriber,
3030
} from '@agoric/notifier';
31-
import type { ChainHub, CosmosChainInfo } from '@agoric/orchestration';
31+
import type {
32+
Bech32Address,
33+
ChainHub,
34+
CosmosChainInfo,
35+
} from '@agoric/orchestration';
3236
import fetchedChainInfo from '@agoric/orchestration/src/fetched-chain-info.js';
3337
import { buildVTransferEvent } from '@agoric/orchestration/tools/ibc-mocks.js';
3438
import { makeTestAddress } from '@agoric/orchestration/tools/make-test-address.js';
@@ -399,7 +403,7 @@ const makeEVM = (template = MockCctpTxEvidences.AGORIC_PLUS_OSMO()) => {
399403

400404
const makeTx = (
401405
amount: bigint,
402-
recipientAddress: string,
406+
recipientAddress: Bech32Address,
403407
nonceOverride?: number,
404408
): CctpTxEvidence => {
405409
nonce += 1;

packages/fast-usdc-contract/test/fixtures.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { buildVTransferEvent } from '@agoric/orchestration/tools/ibc-mocks.js';
22
import fetchedChainInfo from '@agoric/orchestration/src/fetched-chain-info.js';
3-
import type { CosmosChainAddress } from '@agoric/orchestration';
3+
import type { Bech32Address, CosmosChainAddress } from '@agoric/orchestration';
44
import type { VTransferIBCEvent } from '@agoric/vats';
55
import {
66
MockCctpTxEvidences,
@@ -21,9 +21,9 @@ const nobleDefaultVTransferParams = {
2121

2222
export const MockVTransferEvents: Record<
2323
MockScenario,
24-
(receiverAddress?: string) => VTransferIBCEvent
24+
(receiverAddress?: Bech32Address) => VTransferIBCEvent
2525
> = {
26-
AGORIC_PLUS_OSMO: (receiverAddress?: string) =>
26+
AGORIC_PLUS_OSMO: (receiverAddress?: Bech32Address) =>
2727
buildVTransferEvent({
2828
...nobleDefaultVTransferParams,
2929
amount: MockCctpTxEvidences.AGORIC_PLUS_OSMO().tx.amount,
@@ -32,7 +32,7 @@ export const MockVTransferEvents: Record<
3232
receiverAddress ||
3333
MockCctpTxEvidences.AGORIC_PLUS_OSMO().aux.recipientAddress,
3434
}),
35-
AGORIC_PLUS_DYDX: (receiverAddress?: string) =>
35+
AGORIC_PLUS_DYDX: (receiverAddress?: Bech32Address) =>
3636
buildVTransferEvent({
3737
...nobleDefaultVTransferParams,
3838
amount: MockCctpTxEvidences.AGORIC_PLUS_DYDX().tx.amount,
@@ -41,7 +41,7 @@ export const MockVTransferEvents: Record<
4141
receiverAddress ||
4242
MockCctpTxEvidences.AGORIC_PLUS_DYDX().aux.recipientAddress,
4343
}),
44-
AGORIC_PLUS_AGORIC: (receiverAddress?: string) =>
44+
AGORIC_PLUS_AGORIC: (receiverAddress?: Bech32Address) =>
4545
buildVTransferEvent({
4646
...nobleDefaultVTransferParams,
4747
amount: MockCctpTxEvidences.AGORIC_PLUS_AGORIC().tx.amount,
@@ -50,7 +50,7 @@ export const MockVTransferEvents: Record<
5050
receiverAddress ||
5151
MockCctpTxEvidences.AGORIC_PLUS_AGORIC().aux.recipientAddress,
5252
}),
53-
AGORIC_NO_PARAMS: (receiverAddress?: string) =>
53+
AGORIC_NO_PARAMS: (receiverAddress?: Bech32Address) =>
5454
buildVTransferEvent({
5555
...nobleDefaultVTransferParams,
5656
amount: MockCctpTxEvidences.AGORIC_NO_PARAMS().tx.amount,
@@ -59,7 +59,7 @@ export const MockVTransferEvents: Record<
5959
receiverAddress ||
6060
MockCctpTxEvidences.AGORIC_NO_PARAMS().aux.recipientAddress,
6161
}),
62-
AGORIC_UNKNOWN_EUD: (receiverAddress?: string) =>
62+
AGORIC_UNKNOWN_EUD: (receiverAddress?: Bech32Address) =>
6363
buildVTransferEvent({
6464
...nobleDefaultVTransferParams,
6565
amount: MockCctpTxEvidences.AGORIC_UNKNOWN_EUD().tx.amount,
@@ -69,7 +69,7 @@ export const MockVTransferEvents: Record<
6969
MockCctpTxEvidences.AGORIC_UNKNOWN_EUD().aux.recipientAddress,
7070
}),
7171

72-
AGORIC_PLUS_ETHEREUM: (receiverAddress?: string) =>
72+
AGORIC_PLUS_ETHEREUM: (receiverAddress?: Bech32Address) =>
7373
buildVTransferEvent({
7474
...nobleDefaultVTransferParams,
7575
amount: MockCctpTxEvidences.AGORIC_PLUS_ETHEREUM().tx.amount,

0 commit comments

Comments
 (0)