Skip to content
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

feat: ibc transfers #63

Merged
merged 11 commits into from
Mar 16, 2023
Merged

feat: ibc transfers #63

merged 11 commits into from
Mar 16, 2023

Conversation

samsiegart
Copy link
Contributor

@samsiegart samsiegart commented Mar 14, 2023

Fixes: #50

WARNING

This component always attempts to make transactions on mainnet, regardless of if the UI config is set to a local/test network.

Testing

Connected to local chain with same keplr account that has tokens on mainnet. Typed setPreviewEnabled(true) in the console. Sent real tokens over IBC and saw that my balances were updated in Keplr.

TODO (Done)

Since this isn't testable outside of production, we can do one of the following:

  1. Hide this feature behind a feature flag, and/or only show it if mainnet is selected, to avoid confusion.
  2. Keep this as a draft until completion of allow agoric run add-collateral-core.js to work without a solo agoric-sdk#7104 (which at this time is not estimated). We'll then likely have to change the implementation to not rely on wallet/src/util/ibc-assets.ts so arbitrary networks and denoms can be tested.

I'm leaning towards choice 1 because it's much less work (we could call this basically done from here) and we'll have to verify that it works in mainnet anyway.

image
image

@samsiegart samsiegart closed this Mar 14, 2023
@samsiegart samsiegart reopened this Mar 14, 2023
@samsiegart samsiegart marked this pull request as draft March 14, 2023 04:29
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 14, 2023

Deploying with  Cloudflare Pages  Cloudflare Pages

Latest commit: 5cd04af
Status:⚡️  Build in progress...

View logs

@samsiegart
Copy link
Contributor Author

@turadg @michaelfig @dckc Please have a read of the PR description and let me know your thoughts. I'm also willing to discuss in real-time if helpful.

dckc
dckc previously requested changes Mar 14, 2023
Copy link
Member

@dckc dckc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice to see this taking shape!

I haven't managed to really try it out.

I think the assertIsDeliverTxSuccess call is critical.

The rest are just suggestions to think about.

import { SigningStargateClient } from '@cosmjs/stargate';
import { CircularProgress, Link, Snackbar, Typography } from '@mui/material';

export enum IbcDirection {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather we stuck to the erase-to-js subset of ts and avoid enum

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does go for agoric-sdk because we want to avoid a build step. Browser apps will always have a build step so for these repos we (frontend developers) decided to allow enum.

@@ -0,0 +1,3 @@
export const wellKnownPetnames = {
IbcATOM: 'ATOM',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect the vbank name in production to be ATOM.

We can/should change that in our main configs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just changed everything to reference 'ATOM' and do the 'IbcATOM' -> 'ATOM' conversion in the beginning when the purse is loaded.

Comment on lines +43 to +44
denom:
'ibc/BA313C4A19DFBF943586C0387E6B11286F9E416B4DD27574E6909CABE0E342FA',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the denom should be available via agoricNames.vbankAssets. I suppose what's deployed doesn't line up, so I can see why you would hard-code this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, maybe we can remove this in favor of that later, but for now this is needed for testing

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please an XXX comment to that effect

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

chainInfo: {
chainName: 'Cosmos Hub',
chainId: 'cosmoshub-4',
rpc: 'https://cosmoshub-rpc.stakely.io/',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder about fail-over. I suppose stakely's service does some of that internally, but it's still a single point of failure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes good point, it would be good to use an RPC we have some sort of relationship with, ideally an SLA.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree but not a blocker. please file a ticket for future work (e.g.: fall back if Stakely LB is down)

Copy link
Contributor

@arirubinstein arirubinstein Mar 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need autodiscovery of rpc nodes, and client qualification and selection from the chain registry. Any other approach is not ideal. If we're going to hardcode something, perhaps polkachu's node

Copy link
Member

@dckc dckc Mar 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not entirely sure I understand autodiscovery, but since we need it...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like polkachu is locked down:

Access to fetch at 'https://cosmos-rpc.polkachu.com/' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.

Do you have any pointers to how autodiscovery of rpc nodes could work?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this has it's own ticket I'm not considering it to be a blocker for "feat: ibc transfers"

@@ -2,6 +2,7 @@ export const icons = {
IST: 'tokens/IST.png',
BLD: 'tokens/BLD.svg',
ATOM: 'tokens/cosmos.svg',
IbcATOM: 'tokens/cosmos.svg',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might as well kludge whatever code reads from agoricNames.vbankAssets to you can avoid this sort of thing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

close();
showSnackbar(
<>
Successfully executed transaction{' '}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at sendIbcTokens, it doesn't seem to do assertIsDeliverTxSuccess(tx) so I think we need to do that before declaring success.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

setInProgress(false);
}
} else {
/* Is Withdrawal */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comments like this suggest breaking out a named function

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, but kept the comment

try {
const { prefix } = fromBech32(remoteChainAddress);
return prefix !== ibcAsset?.chainInfo.addressPrefix;
} catch (e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lint rules allow unused vars without a special name like _e?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently, just fixed this instance though

try {
const res = await signer.sendIbcTokens(
remoteChainAddress,
keplrConnection.address,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we reacting to the user changing to a different account?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This always uses the agoric address that the wallet UI config is currently using. If the user switches keplr accounts after initially loading the wallet UI, I don't know if we handle that well currently, they'd probably have to refresh the page. Otherwise any transactions would just fail to sign.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they'd probably have to refresh the page

IOU an issue.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully executed transaction{' '}
<Link
color="rgb(0, 176, 255)"
href={`https://bigdipper.live/agoric/transactions/${res.transactionHash}`}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The url seems a little buried; hoist it up near const agoricRpc = ...?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this url into a single util function so it's not repeated. It's a bit tricky to use a const because of the interpolation.

@turadg
Copy link
Member

turadg commented Mar 14, 2023

I'm leaning towards choice 1 because it's much less work (we could call this basically done from here) and we'll have to verify that it works in mainnet anyway.

+1 to this choice and rationale. Long lived PRs tend to rot.

@dckc
Copy link
Member

dckc commented Mar 14, 2023

feature flag works for me.

@samsiegart samsiegart marked this pull request as ready for review March 14, 2023 18:58
@samsiegart samsiegart requested a review from dckc March 14, 2023 18:58
};

const Purse = purse => {
const shouldShowIbcTransferButtons =
(keplrConnection?.chainId === agoricChainId || previewEnabled) &&
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can type setPreviewEnabled(true) in the console to enable ibc transfer, otherwise it only shows for mainnet.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider putting that comment into the code. for non-mainnet, what are we previewing? Is there a ticket to support IBC transfer on other nets?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you could say we're previewing a dangerous misleading version of the component that only makes transactions on mainnet. Just made the comment to that effect.

The closest we have to a ticket is probably Agoric/agoric-sdk#7104. But that doesn't seem to capture everything involved with spinning up two chains, making sure the UI has all the references it needs, etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're previewing a dangerous misleading version of the component that only makes transactions on mainnet

Why would we "preview" that? I think we'd need to "preview" some end user feature that will be production-ready eventually and right now isn't. I haven't seen a ticket for the functionality that will be in production at some point so this sounds more like a "development" or "testing" mode flag.

Will leave this to your judgement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea that is a good point. I just don't know if it's worth making a new flag for this case, maybe just rename it to debug or something...

@samsiegart
Copy link
Contributor Author

I'm able to test this locally because the address for an agoric local chain and mainnet is the same for a given keplr account. So, in real life (prod), I sent tokens to an agoric address. Then, with that keplr account, I created a smart wallet on my local agoric network so that it has the same address. The only caveat is that, in the component, the agoric balance reflects what's on my local chain, not prod. But, the cosmos hub balance reflects cosmos mainnet, and signing the transactions works (but signs on mainnet not local net).

import Petname from './Petname';
import type { PurseInfo } from '../service/Offers';
import type { KeplrUtils } from '../contexts/Provider';
import type { Petname as PetnameType } from '@agoric/smart-wallet/src/types';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since Petname is type that spans repos, consider letting it win this naming collision. Petname.tsx could be PetnameSpan.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

ibcAsset.chainInfo.chainId,
);
const accounts = await offlineSigner.getAccounts();
setRemoteChainAddress(accounts[0].address);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is it safe to pick the first? does this code assume the array has only one element? if so best to assert.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea keplr only returns one address. I think an assert might be too harsh but added a warning so we can be aware if some behavior changes, it shouldn't break anything though.

setRemoteChainSigner(offlineSigner);
};

const isRemoteChainAddressInvalid = useMemo(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's more common to check for validity. consider flipping this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

explorerPath: string,
transactionHash: string,
) => {
setSnackbarMessage(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to put this into state. isSnackbarOpen is a signal for whether to render.

Suggested change
setSnackbarMessage(
snackbarMessage = <>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry I don't follow. Are you saying we can use an empty message (maybe null) as a signal rather than tracking isSnackbarOpen separately?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the opposite. Since we have the Boolean state for whether to present a message we don't also need the message itself to be in state. Just make it a let binding in scope.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see, because we only change the content when we change isSnackbarOpen, so it will re-render from that. It just seems a bit hazardous to me in case we ever want the content to change while open, simpler to just keep state in useState.

Also, using null content to hide the snackbar I realized will make the hide animation look choppy.

Copy link
Member

@turadg turadg Mar 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

content to change while open

Yeah if that's a requirement then the content should be in state.

using null content to hide the snackbar I realized will make the hide animation look choppy

Oh, I didn't realize that. Why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since when you set isSnackbarOpen to false it doesn't disappear immediately, it fades out over a short duration. So, while it's fading, the content will disappear, making the layout shift.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah thanks for explaining. the current code makes sense. maybe worth some comments though with this context.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just put all the snackbar stuff in a hook, also added a close button instead of relying on clickaway. Also I combined the state into one useState so that we only call setState once, since it's from an async event this was causing react to render twice unnecessarily.

Comment on lines 276 to 277
} else {
// Is withdrawal.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't need a comment. consider else if (direction === IbcDirection.Withdrawal) and an else for unknown values. alternately switch(direction) and call helper functions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an else if

<CircularProgress size={36} sx={{ p: 1, mx: 1 }} />
) : (
<>
{/* @ts-expect-error 'cancel' is part of our theme */}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

time to put it into the types?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, we'd have to wrap the component and override the MUI types somewhere down the inheritance tree. It's a big pain that MUI doesn't allow custom color names.

};

const Purse = purse => {
const shouldShowIbcTransferButtons =
(keplrConnection?.chainId === agoricChainId || previewEnabled) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider putting that comment into the code. for non-mainnet, what are we previewing? Is there a ticket to support IBC transfer on other nets?

chainInfo: {
chainName: 'Cosmos Hub',
chainId: 'cosmoshub-4',
rpc: 'https://cosmoshub-rpc.stakely.io/',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree but not a blocker. please file a ticket for future work (e.g.: fall back if Stakely LB is down)

Comment on lines +43 to +44
denom:
'ibc/BA313C4A19DFBF943586C0387E6B11286F9E416B4DD27574E6909CABE0E342FA',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please an XXX comment to that effect

wallet/src/util/ibcTransfer.ts Outdated Show resolved Hide resolved

close();
try {
assertIsDeliverTxSuccess(res);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may be more straightforward to use...

export declare function isDeliverTxSuccess(result: DeliverTxResponse): boolean;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks!

@dckc dckc self-requested a review March 14, 2023 20:48
@dckc dckc dismissed their stale review March 14, 2023 20:49

request addressed

@samsiegart samsiegart requested a review from turadg March 14, 2023 23:11

interface Props {
purses: PurseInfo[] | null;
previewEnabled: boolean;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

surprised to see this passed along since it's a global state.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing it as a prop from context allows components to reactively update when you type setPreviewEnabled(true) in the console.

};

const Purse = purse => {
const shouldShowIbcTransferButtons =
(keplrConnection?.chainId === agoricChainId || previewEnabled) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're previewing a dangerous misleading version of the component that only makes transactions on mainnet

Why would we "preview" that? I think we'd need to "preview" some end user feature that will be production-ready eventually and right now isn't. I haven't seen a ticket for the functionality that will be in production at some point so this sounds more like a "development" or "testing" mode flag.

Will leave this to your judgement.

chainInfo: {
chainName: 'Cosmos Hub',
chainId: 'cosmoshub-4',
rpc: 'https://cosmoshub-rpc.stakely.io/',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this has it's own ticket I'm not considering it to be a blocker for "feat: ibc transfers"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

FE for IBC asset transfer
4 participants