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

[DRAFT] Multi chain adapter #500

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft

[DRAFT] Multi chain adapter #500

wants to merge 7 commits into from

Conversation

0xmaayan
Copy link
Collaborator

@0xmaayan 0xmaayan commented Mar 10, 2025

Introducing different packages to handle cross-chain functionalities, that can be used to build and support a cross-chain adapter in any dapp.
Each package can be used as a standalone component, and the dapp should decide what packages it needs following its business logic.

Live example https://aptos-labs.github.io/aptos-wallet-adapter/nextjs-example-testing/swap

wallet-adapter-aggregator:
A folder that contains multiple packages, each represents a Wallet. In addition, it holds a wallet-adapter-aggregator-core package that holds the abstract class that each wallet must implement.
I implemented a conversion layer for each cross-chain wallet, to represent it as the official Aptos wallet standard. There are some issues I have seen, and I am open for any suggestions.

  • The Aptos wallet standard defines input and output types, and expects each feature to follow it. That becomes a bit difficult when trying to convert each wallet function/feature.
  • Overall, for Solana and I believe other future chains we can handle the types, but when it comes to EVM it is more complex. For example, returning AccountInfo from the connect() or getAccount() methods, is almost impossible because 1) this class represents an Aptos account (and therefore a 32 byte account) and 2) there is no way to retrieve the connected account publicKey from the account address (unless you submitted a transaction)

I am open for any implementation suggestions.

cross-chain-core:
A package that holds a cross-chain logic. Currently only provides CCTP transfer logic with Wormhole as the provider. In the Future, we will support more providers (LayerZero, Cellar, etc) and add it to that package.
In addition, this package can expose some related APIs like getAccountUsdcBalance() (still debating whether this should live in this package, or be part of the wallet features).

The CrossChainCore class accepts different dapp properties

  • network: Network; // the network to use
  • disableTelemetry?: boolean;

And the startCCTPTransfer() method in the WormholeProvider class accepts

  • sourceChain: Chain;
  • wallet: AdapterWallet;
  • destinationAddress: AccountAddressInput;
  • mainSigner: Account;
  • sponsorAccount?: Account | Partial<Record<Network, string>>; // a generated account or gas station key

cross-chain-react
A React state provider that uses

  • The cross-chain-core package
  • The wallet aggregator package the dapp wants to support

Next steps:

@BriungRi
Copy link
Contributor

You can simplify the nextjs example by having the button for Sepolia Solana or Sui open the connect wallet modal for those wallets


export type AptosAccount = Account;

export type Chain = "Solana" | "Ethereum" | "Sui" | "Aptos";
Copy link
Contributor

Choose a reason for hiding this comment

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

why not enum?

export type UsdcBalance = {
amount: string;
decimal: number;
display: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure I would implement display in this component. I think better to let the developer choose how they want to display. I think it just gets complicated because

  1. Developer might want to only use period to dilineate decimals, while others may want to use comma
  2. Developer might want to use some kind of significant figures logic

Just think it's not a good idea to put it in this package because it is hard to undo these decisions later


import { ChainsConfig } from "../types";

export const mainnetChains: ChainsConfig = {
Copy link
Contributor

Choose a reason for hiding this comment

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

any reference link to where these values come from? Would be nice to leave as comments

icon: `data:image/svg+xml;base64,${string}`;
rdns: string;
};
provider: any;
Copy link
Contributor

Choose a reason for hiding this comment

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

uhm?

Copy link
Collaborator Author

@0xmaayan 0xmaayan Mar 12, 2025

Choose a reason for hiding this comment

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

yea, this should be

interface EIP1193Provider {
  on: (event: string, callback: (...args: any[]) => void) => void;
  request: (request: {
    method: string;
    params?: Array<unknown>;
  }) => Promise<any>;
}

Comment on lines 31 to 43
export abstract class AdapterWallet<
GetAccountOutput = any,
GetConnectedNetworkOutput = any,
ConnectOutput = any,
SignMessageInput = any,
SignMessageOutput = any,
SignTransactionInput = any,
SignTransactionOutput = any,
OnAccountChangeInput = any,
OnNetworkChangeInput = any,
SendTransactionInput = any,
SendTransactionOutput = any,
> extends EventEmitter<WalletEvents> {
Copy link
Contributor

Choose a reason for hiding this comment

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

What utility does this wrapper class provide?
Wouldn't it be easier to just map the source chain adapter features into Aptos features?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Standard function types and signature makes it hard to implement source wallet methods as aptos standard methods

Comment on lines 106 to 108
const EIP6963_ADDITIONAL_FEATURES = (
eip6963Wallet: EIP6963ProviderInfo
): Eip6963Features => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need these?
The source wallet already supports eip6963 features, why are we re-defining them?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Standard function types and signature makes it hard to implement source wallet methods as aptos standard methods

Comment on lines 70 to 109
export type Eip6963AccountInfo = {
address: string;
publicKey: Uint8Array<ArrayBufferLike>;
};

export type Eip6963Features = {
"eip6963:connect": {
connect: () => Promise<UserResponse<Eip6963AccountInfo>>;
version: string;
};
"eip6963:account": {
account: () => Promise<Eip6963AccountInfo>;
version: string;
};
"eip6963:sendTransaction": {
sendTransaction: (
transaction: TransactionRequest,
provider: ethers.BrowserProvider
) => Promise<UserResponse<string>>;
version: string;
};
"eip6963:onAccountChange": {
onAccountChange: (
callback: (newAccount: Eip6963AccountInfo) => void
) => void;
version: string;
};
"eip6963:signMessage": {
signMessage: (message: string) => Promise<UserResponse<Uint8Array>>;
version: string;
};
"eip6963:network": {
network: () => Promise<number>;
version: string;
};
// "eip6963:getUsdcBalance": {
// getUsdcBalance: () => Promise<UsdcBalance>;
// version: string;
// };
};
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit confused as to why we need these.
I thought that the goal was to create an abstracted Aptos Account from a source chain.
How are these features needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Global DAA is still in the exploration path, and it most needed and relevant to a specific dapp we are building which will use a dapp specific DAA implementation.

Still, dapps need a way to communicate with the source wallet default methods, so we need to support it

@@ -2,11 +2,11 @@ const isProd = process.env.NODE_ENV === "production";

/** @type {import('next').NextConfig} */
const nextConfig = {
output: "export",
output: "npx serve@latest out",
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this for?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

when starting nextjs build locally next start, it does not support export anymore

sourceChain,
wallet,
destinationAddress:
"0x38383091fdd9325e0b8ada990c474da8c7f5aa51580b65eb477885b6ce0a36b7",
Copy link
Contributor

Choose a reason for hiding this comment

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

is this intended to be hardcoded?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

just for now, eventually the dapp provides the destination account - either DAA or any other Aptos account

);

const resolver = this._wormholeContext.resolver([
routes.CCTPRoute, // manual CCTP
Copy link
Contributor

Choose a reason for hiding this comment

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

wondering what are the other options?
Is there a documentation page for the SDK?

Copy link
Contributor

Choose a reason for hiding this comment

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

can the resolver be cached with the context?
or is it expected to be obtained for each transfer through context.resolver(...)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

wondering what are the other options?
For now, Wormhole supports only Manual CCTP for Aptos

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

can the resolver be cached with the context?
can be cached

]);

const route = await resolver.findRoutes(req);
const cctpRoute = route[0];
Copy link
Contributor

Choose a reason for hiding this comment

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

assuming first route is always best?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes

@hardsetting
Copy link
Contributor

Took a good look, great work!
I have some high-level ideas I'd love to chat about when you have time

The flow I was hoping to see was something similar to this (hopefully you have access):
https://excalidraw.com/#json=dtzELJ8ezex0lWN18D-F1,3dKp9B39GvEuf4tifqTOQw

@0xmaayan
Copy link
Collaborator Author

You can simplify the nextjs example by having the button for Sepolia Solana or Sui open the connect wallet modal for those wallets

That brings up questions around multi chain wallets. For example, Nightly which is a wallet that supports all those chains.
I rather leave it to the dapp to decide how it wants to display the wallets in its own WalletSelector implementation

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.

4 participants