-
Notifications
You must be signed in to change notification settings - Fork 214
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
167 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/** | ||
* @import {MinimalNetworkConfig} from '@agoric/client-utils'; | ||
*/ | ||
|
||
import { StargateClient } from '@cosmjs/stargate'; | ||
|
||
/** | ||
* @param {MinimalNetworkConfig} config | ||
* @returns {Promise<StargateClient>} | ||
*/ | ||
export const makeStargateClient = async config => { | ||
// TODO distribute load | ||
const endpoint = config.rpcAddrs.at(-1); | ||
assert(endpoint, 'no endpoints'); | ||
|
||
return StargateClient.connect(endpoint); | ||
}; | ||
|
||
/** | ||
* @param {{ | ||
* client: StargateClient, | ||
* delay: (ms: number) => Promise<void>, | ||
* period?: number, | ||
* retryMessage?: string, | ||
* }} opts | ||
* @returns {<T>(l: (b: { time: string, height: number }) => Promise<T>) => Promise<T>} | ||
*/ | ||
export const pollBlocks = opts => async lookup => { | ||
const { client, delay, period = 3 * 1000 } = opts; | ||
const { retryMessage } = opts; | ||
|
||
await null; // separate sync prologue | ||
|
||
for (;;) { | ||
const status = await client.getBlock(); | ||
const { | ||
header: { time, height }, | ||
} = status; | ||
try { | ||
// see await null above | ||
const result = await lookup({ time, height }); | ||
return result; | ||
} catch (_err) { | ||
console.error( | ||
time, | ||
retryMessage || 'not in block', | ||
height, | ||
'retrying...', | ||
); | ||
await delay(period); | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from './format.js'; | ||
export * from './rpc.js'; | ||
export * from './wallet-utils.js'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { makeWalletStateCoalescer } from '@agoric/smart-wallet/src/utils.js'; | ||
import { makeStargateClient, pollBlocks } from './chain.js'; | ||
import { boardSlottingMarshaller, makeRpcUtils } from './rpc.js'; | ||
|
||
/** | ||
* @import {Amount, Brand} from '@agoric/ertp/src/types.js' | ||
*/ | ||
|
||
export const makeWalletUtils = async ({ fetch, delay }, networkConfig) => { | ||
const { agoricNames, fromBoard, marshaller, readLatestHead, vstorage } = | ||
await makeRpcUtils({ fetch }, networkConfig); | ||
|
||
const client = await makeStargateClient(networkConfig); | ||
|
||
/** | ||
* @param {string} from | ||
* @param {number|string} [minHeight] | ||
*/ | ||
const storedWalletState = async (from, minHeight = undefined) => { | ||
const m = boardSlottingMarshaller(fromBoard.convertSlotToVal); | ||
|
||
const history = await vstorage.readFully( | ||
`published.wallet.${from}`, | ||
minHeight, | ||
); | ||
|
||
/** @type {{ Invitation: Brand<'set'> }} */ | ||
// @ts-expect-error XXX how to narrow AssetKind to set? | ||
const { Invitation } = agoricNames.brand; | ||
const coalescer = makeWalletStateCoalescer(Invitation); | ||
// update with oldest first | ||
for (const txt of history.reverse()) { | ||
const { body, slots } = JSON.parse(txt); | ||
const record = m.fromCapData({ body, slots }); | ||
coalescer.update(record); | ||
} | ||
const coalesced = coalescer.state; | ||
harden(coalesced); | ||
return coalesced; | ||
}; | ||
|
||
/** | ||
* Get OfferStatus by id, polling until available. | ||
* | ||
* @param {string} from | ||
* @param {string|number} id | ||
* @param {number|string} minHeight | ||
* @param {boolean} [untilNumWantsSatisfied] | ||
*/ | ||
const pollOffer = async ( | ||
from, | ||
id, | ||
minHeight, | ||
untilNumWantsSatisfied = false, | ||
) => { | ||
const poll = pollBlocks({ | ||
client, | ||
delay, | ||
retryMessage: 'offer not in wallet at block', | ||
}); | ||
|
||
const lookup = async () => { | ||
const { offerStatuses } = await storedWalletState(from, minHeight); | ||
const offerStatus = [...offerStatuses.values()].find(s => s.id === id); | ||
if (!offerStatus) throw Error('retry'); | ||
harden(offerStatus); | ||
if (untilNumWantsSatisfied && !('numWantsSatisfied' in offerStatus)) { | ||
throw Error('retry (no numWantsSatisfied yet)'); | ||
} | ||
return offerStatus; | ||
}; | ||
return poll(lookup); | ||
}; | ||
|
||
/** | ||
* @param {string} addr | ||
* @returns {Promise<import('@agoric/smart-wallet/src/smartWallet.js').UpdateRecord>} | ||
*/ | ||
const getLastUpdate = addr => { | ||
// @ts-expect-error cast | ||
return readLatestHead(`published.wallet.${addr}`); | ||
}; | ||
|
||
return { | ||
networkConfig, | ||
agoricNames, | ||
fromBoard, | ||
marshaller, | ||
vstorage, | ||
getLastUpdate, | ||
readLatestHead, | ||
storedWalletState, | ||
pollOffer, | ||
}; | ||
}; | ||
/** @typedef {Awaited<ReturnType<typeof makeWalletUtils>>} WalletUtils */ |