-
Notifications
You must be signed in to change notification settings - Fork 206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(smart-wallet): create purses for new assets lazily #6774
Conversation
I like the suggestion at the end. The first part means we're in a bit of a bind. If we can query balances for different denoms outside of vstorage, like on the cosmos account level, we can mesh assets with and without purses seamlessly in the UI maybe? |
@@ -135,7 +134,7 @@ const mapToRecord = map => Object.fromEntries(map.entries()); | |||
* @returns {State} | |||
*/ | |||
export const initState = (unique, shared) => { | |||
// Some validation of inputs. "any" erefs because this synchronous call can't check more than that. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why remove this explanation for M.any
?
btw, should some of these M.remotable
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why remove this explanation for
M.any
?
Because I got rid of all the M.any
cases. Oops... but then I reverted that change without putting the comment back.
btw, should some of these
M.remotable
?
Yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refining the typeguards isn't necessary to fix #6652, so arguably it should go in a separate PR. You don't mind if I include it here, do you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM, makes sense with the anys gone :)
// async so we consistently return a promise | ||
purseForBrand: async brand => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alternately, in a way that doesn't need explaining:
// async so we consistently return a promise | |
purseForBrand: async brand => { | |
/** @returns {Promise<Brand>} */ | |
purseForBrand: brand => { |
then the return brandPurses.get(brand);
would have a squiggly and force,
return Promise.resolve(brandPurses.get(brand));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That exposed a conflict with withdrawGive
where the purse for a brand is expected to be available synchronously. Ugh.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah... it's ok to have promises in keywordPaymentPromises
...
@@ -490,35 +481,20 @@ const SmartWalletKit = defineVirtualFarClassKit( | |||
}, | |||
{ | |||
finish: ({ state, facets }) => { | |||
const { invitationBrand, invitationIssuer, invitationPurse, bank } = | |||
state; | |||
const invitationDisplayInfo = { assetKind: AssetKind.SET }; // XXX |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what should this be instead?
maybe a Zoe const?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A Zoe const might work... but I went with the caller passing it in along with the brand and issuer.
}, | ||
// @ts-expect-error cast to RemotePurse | ||
/** @type {RemotePurse} */ (invitationPurse), | ||
); | ||
// watch the bank for new issuers to make purses out of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👌
/** @type {import('ava').TestFn<Awaited<ReturnType<makeDefaultTestContext>>>} */ | ||
const test = anyTest; | ||
|
||
const TODO = undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
turns out undefined
works, so I inlined it as noBridge
.
}); | ||
t.true( | ||
exp.addedWrites <= (base.addedWrites * exp.qty) / base.qty / 2, | ||
'actual writes should be less than half of linear growth', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice
subscribed = true; | ||
}; | ||
|
||
// XXX this isn't far, is it? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it doesn't need to be, because all the wallets are in the same vat. We've talked about separating them. Any reason not to Far it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its clients make synchronous calls on it. So they have to be in the same vat.
E(board).getReadonlyMarshaller(), | ||
]); | ||
|
||
const makeAssetRegistry = () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this has access to all of start
scope, making it a little harder to read and audit.
please move this function def to module scope or another file. consider unit testing it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hosting it is a good idea... done.
As to unit testing... I'm inclined to let the existing tests suffice. Mocking an anyBank
would be tedious, and using the existing vbank code would be more or less another integration test like the others.
01e3a0a
to
4c5b1bc
Compare
@arirubinstein this does include a simple test ( |
Yes, that's straightforward. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. I assume it won't merge until fixups are rebased and you have some of the other reviews you're seeking
const terms = await deeplyFulfilled( | ||
harden({ | ||
agoricNames, | ||
board, | ||
anyBank: poolBank, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm. This gives the walletFactory authority to spend from the poolBank
. That's not great. Gotta think that over...
I intend to squash it all to one commit. |
e688aaf
to
53e7d7b
Compare
const desc = registry.getRegisteredAsset(brand); | ||
/** @type {RemotePurse} */ | ||
// @ts-expect-error cast to RemotePurse | ||
const purse = E(desc.issuer).makeEmptyPurse(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this needs to make a vbank purse a la E(bank).getPurse(desc.brand)
8641317
to
d5c5c45
Compare
@Mergifyio refresh |
✅ Pull request refreshed |
@Mergifyio unqueue |
✅ The pull request has been removed from the queue |
@dckc the mergify bot snafu might make it hard to land this PR. Please ping me if re-adding a label doesn't work (You might also try the bot command) |
failing test case for #6652
- subscribe for new vbank assets once, in walletFactory.js - provide a read-only registry of the assets - no change to RPC protocol: still publish all brand descriptors in all wallet states - refactor: clarify invitationIssuer promise - add invitationDisplayInfo to synchronously-provided SharedParams - refine typeguards, esp. M.any() => M.remotable('...') - hoist makeAssetRegistry() to module scope to reduce in-scope authority feat(smart-wallet): initialize with existing vbank assets - add anyBank to walletFactory terms so we can subscribe to vbank assets without waiting for creation of the 1st wallet - refine customTermsShape from M.not(M.undefined()) to M.eref(M.remotable()) - attenuate poolBank to {getAssetSubscription} - 'want stable' test: don't assume incremental updates include all assets; get the full current state to start with - constrain BrandDescriptor to take settled issuers, since publishing records that include promises doesn't let off-chain callers compare identities - losen addBrand() typeguard to allow M.eref(PurseShape) since we await the purse inside the function - static type for purseForBrand motivates tweak to keywordPaymentPromises - fixup: Promise<NameHub> isn't durable a promise for agoricNames isn't durable. so I need deeplyFulfilledObject after all. but the types don't work out. so back to deeplyFulfilled I guess
d5c5c45
to
e241ba0
Compare
refs: #6652
Description
walletFactory.js
.purseForBrand
given tomakeOfferExecutor
to create purses lazily.Security Considerations
When a user executes an offer, if we don't have a purse for one of the relevant brands, we take the brand they give and "upgrade" it to an issuer, by way of info we got from the vbank, in order to make a purse. Normally we would expect the user to explicitly indicate the issuers that they trust. This isn't so much a change in the security properties of the contract - it trusted the vbank to provide issuers to make purses before - but this now happens in response to the user placing an offer rather than in response to the vbank announcing a new asset.
Documentation Considerations
We should tell users that if a vbank asset is added after they provision their smart wallet, they won't see a purse until they execute an offer that uses it.
Testing Considerations
I'm somewhat surprised at how little code needed to change for this in order to get
yarn test
inpackages/smart-wallet
to pass. I wonder whether those tests have good coverage of executing offers.p.s. tests in inter-protocol provide more coverage.