Skip to content

Commit

Permalink
shuffle channel config and provide a method to accurately determine i…
Browse files Browse the repository at this point in the history
…f a chain is gateway enabled
  • Loading branch information
barnjamin committed Oct 13, 2023
1 parent 65c5fb4 commit 11ab40c
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 144 deletions.
2 changes: 0 additions & 2 deletions connect/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ export * as circle from "./circle-api";
export * as api from "./api";

// Re-export from core packages

/** @namespace */
export {
contracts,
Chain,
Expand Down
74 changes: 43 additions & 31 deletions connect/src/protocols/gatewayTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ import {
UnsignedTransaction,
VAA,
WormholeMessageId,
asGatewayMsg,
toGatewayMsg,
deserialize,
gatewayTransferMsg,
isGatewayTransferDetails,
isTransactionIdentifier,
isWormholeMessageId,
toNative,
IbcBridge,
nativeChainAddress,
} from "@wormhole-foundation/sdk-definitions";
import { Wormhole } from "../wormhole";
import {
Expand All @@ -43,7 +44,11 @@ export class GatewayTransfer implements WormholeTransfer {

private readonly wh: Wormhole;

private readonly wc: ChainContext<PlatformName>;
// Wormchain context
private readonly gateway: ChainContext<PlatformName>;
// Wormchain IBC Bridge
private readonly gatewayIbcBridge: IbcBridge<PlatformName>;
// Contract address
private readonly gatewayAddress: ChainAddress;

// state machine tracker
Expand All @@ -70,7 +75,12 @@ export class GatewayTransfer implements WormholeTransfer {
// Any transfers we do over ibc
ibcTransfers?: IbcTransferInfo[];

private constructor(wh: Wormhole, transfer: GatewayTransferDetails) {
private constructor(
wh: Wormhole,
transfer: GatewayTransferDetails,
gateway: ChainContext<PlatformName>,
gatewayIbc: IbcBridge<PlatformName>,
) {
this.state = TransferState.Created;
this.wh = wh;
this.transfer = transfer;
Expand All @@ -86,7 +96,10 @@ export class GatewayTransfer implements WormholeTransfer {
};

// cache the wormchain chain context since we need it for checks
this.wc = this.wh.getChain(GatewayTransfer.chain);
this.gateway = gateway;
this.gatewayIbcBridge = gatewayIbc;

// cache the message since we don't want to regenerate it any time we need it
this.msg = gatewayTransferMsg(this.transfer);
}

Expand All @@ -111,9 +124,13 @@ export class GatewayTransfer implements WormholeTransfer {
wh: Wormhole,
from: GatewayTransferDetails | WormholeMessageId | TransactionId,
): Promise<GatewayTransfer> {
// we need this regardless of the type of `from`
const wc = wh.getChain(GatewayTransfer.chain);
const wcibc = await wc.getIbcBridge();

// Fresh new transfer
if (isGatewayTransferDetails(from)) {
return new GatewayTransfer(wh, from);
return new GatewayTransfer(wh, from, wc, wcibc);
}

// Picking up where we left off
Expand All @@ -129,7 +146,7 @@ export class GatewayTransfer implements WormholeTransfer {
throw new Error("Invalid `from` parameter for GatewayTransfer");
}

const gt = new GatewayTransfer(wh, gtd);
const gt = new GatewayTransfer(wh, gtd, wc, wcibc);
gt.transactions = txns;

// Since we're picking up from somewhere we can move the
Expand Down Expand Up @@ -167,13 +184,13 @@ export class GatewayTransfer implements WormholeTransfer {
// we need to preserve it
let nonce: number | undefined;

let to = { ...vaa.payload.to };
let to: ChainAddress = { ...vaa.payload.to };
// The payload here may be the message for Gateway
// Lets be sure to pull the real payload if its set
// Otherwise revert to undefined
if (payload) {
try {
const maybeWithPayload = asGatewayMsg(Buffer.from(payload).toString());
const maybeWithPayload = toGatewayMsg(Buffer.from(payload).toString());
nonce = maybeWithPayload.nonce;
payload = maybeWithPayload.payload
? new Uint8Array(Buffer.from(maybeWithPayload.payload))
Expand All @@ -185,13 +202,9 @@ export class GatewayTransfer implements WormholeTransfer {
"base64",
).toString();

to = {
chain: destChain,
// @ts-ignore
address: toNative(destChain, recipientAddress),
};
} catch (e) {
// Ignoring, throws if not the payload isnt JSON
to = nativeChainAddress([destChain, recipientAddress]);
} catch {
/*Ignoring, throws if not the payload isnt JSON*/
}
}

Expand Down Expand Up @@ -244,7 +257,7 @@ export class GatewayTransfer implements WormholeTransfer {
address: toNative(xfer.id.chain, xfer.data.denom),
} as TokenId;

const msg = asGatewayMsg(xfer.data.memo);
const msg = toGatewayMsg(xfer.data.memo);
const destChain = toChainName(msg.chain);

// TODO: sure it needs encoding?
Expand Down Expand Up @@ -401,8 +414,6 @@ export class GatewayTransfer implements WormholeTransfer {
const attestations: AttestationId[] = [];
this.ibcTransfers = [];

const wcIbc = await this.wc.getIbcBridge();

// collect ibc transfers and additional transaction ids
if (this.fromGateway()) {
// assume all the txs are from the same chain
Expand Down Expand Up @@ -433,7 +444,8 @@ export class GatewayTransfer implements WormholeTransfer {

// now find the corresponding wormchain transaction given the ibcTransfer info
const retryInterval = 5000;
const task = () => wcIbc.lookupMessageFromIbcMsgId(xfer.id);
const task = () =>
this.gatewayIbcBridge.lookupMessageFromIbcMsgId(xfer.id);
const whm = await retry<WormholeMessageId>(task, retryInterval);
if (!whm)
throw new Error(
Expand Down Expand Up @@ -472,7 +484,7 @@ export class GatewayTransfer implements WormholeTransfer {

// Wait until the vaa is redeemed before trying to look up the
// transfer message
const wcTb = await this.wc.getTokenBridge();
const wcTb = await this.gateway.getTokenBridge();
// Since we want to retry until its redeemed, return null
// in the case that its not redeemed
const isRedeemedTask = async () => {
Expand All @@ -487,7 +499,8 @@ export class GatewayTransfer implements WormholeTransfer {
// Note: Because we search by GatewayTransferMsg payload
// there is a possibility of dupe messages being returned
// using a nonce should help
const wcTransferTask = () => fetchIbcXfer(wcIbc, this.msg);
const wcTransferTask = () =>
fetchIbcXfer(this.gatewayIbcBridge, this.msg);
const wcTransfer = await retry<IbcTransferInfo>(
wcTransferTask,
retryInterval,
Expand Down Expand Up @@ -588,18 +601,17 @@ export class GatewayTransfer implements WormholeTransfer {
throw new Error(`No serde defined for type: ${partial.payload[0]}`);
}

// TODO: Is this a good enough check for what we want to do?
// Implicitly determine if the chain is Gateway enabled by
// checking to see if the Gateway IBC bridge has a transfer channel setup
private fromGateway(): boolean {
//IbcBridge.getChannels();

return chainToPlatform(this.transfer.from.chain) === "Cosmwasm";
// return networkChainToChannelId.has(
// this.wh.network,
// this.transfer.from.chain,
// );
return (
this.gatewayIbcBridge.getTransferChannel(this.transfer.from.chain) !==
null
);
}
private toGateway(): boolean {
return chainToPlatform(this.transfer.to.chain) === "Cosmwasm";
// return networkChainToChannelId.has(this.wh.network, this.transfer.to.chain);
return (
this.gatewayIbcBridge.getTransferChannel(this.transfer.to.chain) !== null
);
}
}
27 changes: 9 additions & 18 deletions core/definitions/src/protocols/ibc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,13 @@ export interface GatewayMsg {
// GatewayTransferMsg is the message sent in the payload
// of a TokenTransfer to be executed by the Gateway contract.
export interface GatewayTransferMsg {
gateway_transfer: {
chain: ChainId;
recipient: string;
fee: string;
nonce: number;
};
gateway_transfer: Exclude<GatewayMsg, "payload">;
}

// GatewayTransferWithPayloadMsg is the message sent in the payload of a
// TokenTransfer with its own payload to be executed by the Gateway contract.
export interface GatewayTransferWithPayloadMsg {
gateway_transfer_with_payload: {
chain: ChainId;
recipient: string;
fee: string;
nonce: number;
payload: string;
};
gateway_transfer_with_payload: GatewayMsg;
}

// GatewayIBCTransferMsg is the message sent in the memo of an IBC transfer
Expand Down Expand Up @@ -101,7 +90,7 @@ export function isGatewayTransferDetails(

// Get the underlying payload from a gateway message
// without prefix
export function asGatewayMsg(
export function toGatewayMsg(
msg:
| GatewayTransferMsg
| GatewayTransferWithPayloadMsg
Expand Down Expand Up @@ -151,7 +140,7 @@ export function gatewayTransferMsg(

export function makeGatewayTransferMsg(
chain: ChainName | ChainId,
recipient: NativeAddress<"Cosmwasm"> | string,
recipient: NativeAddress<PlatformName> | string,
fee: bigint = 0n,
nonce: number,
payload?: string,
Expand All @@ -161,7 +150,7 @@ export function makeGatewayTransferMsg(
const address =
typeof recipient === "string"
? recipient
: // @ts-ignore
: //@ts-ignore
Buffer.from(recipient.toString()).toString("base64");

const common = {
Expand Down Expand Up @@ -229,8 +218,10 @@ export interface IbcBridge<P extends PlatformName> {
): AsyncGenerator<UnsignedTransaction>;

// cached from config
//getChannels(): IbcChannel | null;
//fetchChannels(): Promise<IbcChannel | null>;
getTransferChannel(chain: ChainName): string | null;

// fetched from contract
fetchTransferChannel(chain: ChainName): Promise<string | null>;

lookupMessageFromIbcMsgId(msg: IbcMessageId): Promise<WormholeMessageId>;

Expand Down
40 changes: 11 additions & 29 deletions platforms/cosmwasm/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,50 +148,32 @@ export const cosmwasmNetworkChainToRestUrls = constMap(
cosmwasmNetworkChainRestUrl,
);

export type IbcChannel = {
srcChannel: string;
dstChannel: string;
};
export type IbcChannels = Partial<Record<CosmwasmChainName, string>>;

// IBC Channels from the perspective of Wormchain
// For each chain, add the channel id for each other chain
const gatewayConnections = [
[
"Mainnet",
[
["Cosmoshub", { srcChannel: "channel-5", dstChannel: "" }],
["Osmosis", { srcChannel: "channel-4", dstChannel: "" }],
],
[["Wormchain", { Cosmoshub: "channel-5", Osmosis: "channel-4" }]],
],
[
"Testnet",
[
[
"Cosmoshub",
{
srcChannel: "channel-5",
dstChannel: "channel-3086",
},
],
[
"Osmosis",
{
srcChannel: "channel-9",
dstChannel: "channel-3906",
},
],
["Wormchain", { Cosmoshub: "channel-5", Osmosis: "channel-9" }],
["Cosmoshub", { Wormchain: "channel-3086" }],
["Osmosis", { Wormchain: "channel-3906" }],
],
],
[
"Devnet",
[
["Cosmoshub", { srcChannel: "", dstChannel: "" }],
["Osmosis", { srcChannel: "", dstChannel: "" }],
["Wormchain", { Cosmoshub: "channel-1", Osmosis: "channel-2" }],
["Cosmoshub", { Wormchain: "channel-1" }],
["Osmosis", { Wormchain: "channel-1" }],
],
],
] as const satisfies RoArray<
readonly [Network, RoArray<readonly [CosmwasmChainName, IbcChannel]>]
readonly [Network, RoArray<readonly [CosmwasmChainName, IbcChannels]>]
>;

export const networkChainToChannelId = constMap(gatewayConnections);
export const networkChannelToChain = constMap(gatewayConnections, [0, [2, 1]]);
export const networkToChannelMap = constMap(gatewayConnections, [0, [1, 2]]);
export const networkChainToChannels = constMap(gatewayConnections);
42 changes: 13 additions & 29 deletions platforms/cosmwasm/src/gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { CosmwasmPlatform } from "./platform";
import { CosmwasmChainName } from "./types";
import { CosmwasmIbcBridge } from "./protocols/ibc";
import { CosmwasmTokenBridge } from "./protocols/tokenBridge";
import { IBC_TRANSFER_PORT } from "./constants";

export module Gateway {
export const name: "Wormchain" = "Wormchain";
Expand Down Expand Up @@ -74,36 +75,22 @@ export module Gateway {
return new CosmwasmAddress(factoryAddress);
}

// Returns the destination channel on wormchain for given source chain
// Returns the destination channel from the perspective of wormchain for given source chain
export function getDestinationChannel(chain: CosmwasmChainName): string {
const channels = CosmwasmPlatform.getIbcChannel(chain);
const channels = CosmwasmPlatform.getIbcChannels(Gateway.name);
if (!channels) throw new Error("No channels configured for chain " + chain);
return channels.dstChannel;
if (!(chain in channels))
throw new Error("No channel configured for chain " + chain);
return channels[chain]!;
}
// Gets the source channel on wormchain for a given chain

// Gets the source channel from the perspective of wormchain for a given chain
export function getSourceChannel(chain: CosmwasmChainName): string {
const channels = CosmwasmPlatform.getIbcChannel(chain);
const channels = CosmwasmPlatform.getIbcChannels(chain);
if (!channels) throw new Error("No channels configured for chain " + chain);
return channels.srcChannel;
}

// Returns whether or not a given chain is gateway supported
export function isSupported(chain: CosmwasmChainName): boolean {
return CosmwasmPlatform.getIbcChannel(chain) !== null;
}

// Derive the Token Address with context for whether or not its managed
// https://github.com/wormhole-foundation/wormhole/blob/251e6c4a6478379ff862aed08d835f9022ef4143/cosmwasm/contracts/token-bridge/src/token_address.rs#L12
export function deriveTokenAddress(
chain: ChainName,
asset: string,
): Uint8Array {
const tokenId = new Uint8Array(32);
//const addr = toNative(chain, asset) as CosmwasmAddress;
const nativeFlg = CosmwasmPlatform.isNativeDenom(chain, asset) ? 1 : 0;
tokenId.set([nativeFlg], 0);
tokenId.set(keccak256(asset).slice(1), 1);
return tokenId;
if (!(Gateway.name in channels))
throw new Error("No channel configured for chain " + chain);
return channels[Gateway.name]!;
}

// derive the ics20 token denom from the
Expand All @@ -112,12 +99,9 @@ export module Gateway {
chain: CosmwasmChainName,
denom: string,
): CosmwasmAddress {
// If its already an IBC token, just return it
if (denom.startsWith("ibc/")) return new CosmwasmAddress(denom);

// Otherwise compute the ibc address from the channel and denom
const channel = getDestinationChannel(chain);
const hashData = Buffer.from(`transfer/${channel}/${denom}`);
const hashData = Buffer.from(`${IBC_TRANSFER_PORT}/${channel}/${denom}`);
const hash = Buffer.from(sha256(hashData)).toString("hex");
return new CosmwasmAddress(`ibc/${hash.toUpperCase()}`);
}
Expand Down
Loading

0 comments on commit 11ab40c

Please sign in to comment.