-
Notifications
You must be signed in to change notification settings - Fork 208
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: chainHub.makeTransferRoute()
#10584
Conversation
Deploying agoric-sdk with Cloudflare Pages
|
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'd like to understand why return undefined
rather than throw
.
* @property {IBCChannelID} sourceChannel | ||
* @property {Coin} token | ||
* @property {string} receiver - destination chain address value | ||
* @property {ForwardInfo} [forwardInfo] - contains pfm forwarding info |
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.
* @property {ForwardInfo} [forwardInfo] - contains pfm forwarding info | |
* @property {ForwardInfo} [forwardInfo] - contains PFM forwarding info |
const TransferRouteShape = M.splitRecord( | ||
{ | ||
sourcePort: M.string(), | ||
sourceChannel: M.string(), |
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.
The type says IBCChannelID
. Do we have a shape for that?
Interesting... the one in orch is marked internal.
/** @internal */
export const IBCChannelIDShape = M.string();
token: { | ||
amount: M.string(), | ||
denom: M.string(), | ||
}, |
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 CoinShape
would be handy... especially to document the strange use of string for amount
.
I hope we're properly defensive about that; that is: when we accept an amount
string argument, we don't assume that it's a properly formatted numeral.
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.
getTransferRoute
(guarded) takes denomArg
which contains a bigint. We cast to a string in this function.
In a future PR, localOrchAccount
will probably trust that chainHub
did this correctly and not verify the string. Alternatively, we can return a DenomAmount
and transform it at the getTransferRoute()
. The former is preferably to me but LMK if you still have concerns.
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.
depends on what localOrchAccount
is relying on chainHub
to do. If it's relying on chainHub
to make data to be sent out, then very well. But if localOrchAccount
is going to parse the numeral, I would expect it to verify with Nat(BigInt(numeral))
.
* `memo` for `MsgTransfer`. | ||
* | ||
* @typedef {object} TransferRoute | ||
* @property {string} sourcePort |
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.
* @property {string} sourcePort | |
* @property {string} sourcePort typically 'transfer' |
* @property {string} sourcePort | ||
* @property {IBCChannelID} sourceChannel | ||
* @property {Coin} token | ||
* @property {string} receiver - destination chain address value |
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.
Is this really any string? I saw a note from @dtribble that some parts of the IBC world check the bech32 checksum of some address fields.
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.
In TS I'm not sure how to express this, maybe @turadg has ideas. Runtime checks are certainly an option but we can also rely on the cosmos layer to surface bech32 errors.
Here, receiver
is not required to be valid bech32
. If we have forwardInfo
this will be "pfm"
. Supposes this part could be expressed in the types. Edit: TransferRoute
now uses a discriminated union, but still takes no opinion on whether a string is bech32.
if (!chainInfos.has(holdingChainName)) { | ||
log(`chain info not found for holding chain: ${holdingChainName}`); | ||
return 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.
Why not throw? Why is it OK for the caller use this with an unregistered chain name?
Likewise for the rest. I can't see any reason that the caller shouldn't establish the preconditions.
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 cam to the same realization shortly after putting this up:
#10584 (comment)
Amended to throw.
receiver: destination.value, | ||
port: issuerToDest.transferChannel.portId, | ||
channel: issuerToDest.transferChannel.channelId, | ||
...{ |
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 the extra nesting?
denom: denomAmount.denom, | ||
}, | ||
/** | ||
* purposely using invalid bech32 |
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 comment is nice... but I'd rather see this comment on the type.
* @param {string} [chainName] | ||
* @param {Record<string, CosmosChainInfo>} [infoOf] | ||
* @param {string} [brandKey] | ||
* @returns {[string, DenomDetail & { brandKey?: string }]} |
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'm not comfortable baking brandKey
into the orch API.
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.
Added a TODO that references #10580
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.
But that would be a breaking change to a high-visibility API.
Is brandKey
essential for this PR? Can we just use brands?
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.
brandKey
is an existing convention from registerChainsAndAssets
, landed on master in e72782d. The ticket mentions the three contracts expecting it their build scripts.
assetOn
is not currently exported from @agoric/orchestration
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'm juggling too much. Somehow I got the impression that brandKey
hadn't escaped fast-usdc.
assetOn
is not exported... ok.
const infoWithBrand = info.brandKey | ||
? { ...info, brand: brands[info.brandKey] } | ||
: info; | ||
const { brandKey, ...rest } = info; | ||
const infoWithBrand = brandKey | ||
? { ...rest, brand: brands[brandKey] } | ||
: rest; |
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 meant to say something about that earlier.
649df1c
to
4bd202a
Compare
chainHub.getTransferRoute()
chainHub.makeTransferRoute()
4bd202a
to
ab62bd1
Compare
export type TransferRoute = | ||
| { | ||
/** typically, `transfer` */ | ||
sourcePort: string; | ||
sourceChannel: IBCChannelID; | ||
token: Coin; | ||
receiver: typeof PFM_RECEIVER; | ||
/** contains PFM forwarding info */ | ||
forwardInfo: ForwardInfo; | ||
} | ||
| { | ||
/** typically, `transfer` */ | ||
sourcePort: string; | ||
sourceChannel: IBCChannelID; | ||
token: Coin; | ||
receiver: string; | ||
}; |
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.
could be a bit more concise:
export type TransferRoute = | |
| { | |
/** typically, `transfer` */ | |
sourcePort: string; | |
sourceChannel: IBCChannelID; | |
token: Coin; | |
receiver: typeof PFM_RECEIVER; | |
/** contains PFM forwarding info */ | |
forwardInfo: ForwardInfo; | |
} | |
| { | |
/** typically, `transfer` */ | |
sourcePort: string; | |
sourceChannel: IBCChannelID; | |
token: Coin; | |
receiver: string; | |
}; | |
export type TransferRoute = { | |
/** typically, `transfer` */ | |
sourcePort: string; | |
sourceChannel: IBCChannelID; | |
token: Coin; | |
} & ( | |
| { | |
receiver: typeof PFM_RECEIVER; | |
/** contains PFM forwarding info */ | |
forwardInfo: ForwardInfo; | |
} | |
| { | |
receiver: string; | |
}); |
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
if (!chainInfos.has(holdingChainName)) { | ||
throw makeError( | ||
`chain info not found for holding chain: ${q(holdingChainName)}`, | ||
); | ||
} |
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.
The usual idiom is:
if (!chainInfos.has(holdingChainName)) { | |
throw makeError( | |
`chain info not found for holding chain: ${q(holdingChainName)}`, | |
); | |
} | |
chainInfos.has(holdingChainName) || Fail`chain info not found for holding chain: ${q(holdingChainName)}`; |
Is this one of those places where the type checker doesn't grok?
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.
Done. There is one condition (getAsset
can return undefined) but I casted
f01678c
to
542c043
Compare
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.
onward...
542c043
to
f1d83a1
Compare
f1d83a1
to
cd59676
Compare
Co-authored-by: Dan Connolly <[email protected]>
- json structure of pfm memo
- also, dont iterate over connections if there arent any
- chainHub helper to return .transfer details for ibc transfer that can have multiple hops (via pfm)
cd59676
to
1717b2a
Compare
refs: #10445
refs: #10006
Description
Adds
.makeTransferRoute(destination: ChainAddress, amount: DenomAmount, holdingChainName: string)
toChainHub
to facilitate building IBCMsgTransfer
parameters for single-hop and multi-hop (pfm) routes.Returns synchronously using local
chainHub
data to facilitate ease of use at call sites and support future plans to make every call synchronous. It achieves this by interfacing with thechainInfos
andconnInfos
map stores directly instead of the public interface methods that currently fall back to remote calls toagoricNames
.Assumes the longest route will only be 1 intermediary hop (through the issuing chain). Does not support unwinding nested denoms (e.g. noble uusdc sent directly from osmosis to agoric
agoric(osmosis(noble(uusdc)))
).Security Considerations
An incorrect implementation could result in loss of funds. #9324 remains open to determine sensible defaults for timeout parameters.
Scaling Considerations
Nothing new, but each contract's
chainHub
will accumulate quite a bit of data .Documentation Considerations
JSdoc and tests
Testing Considerations
Includes unit tests covering all codepaths and known scenarios.
Upgrade Considerations
N/A, library code. This feature is needed for FUSDC.