From 0c314f1965f71c2ae7cd52d72415450e9970f280 Mon Sep 17 00:00:00 2001 From: Kaan Caglan Date: Fri, 28 Feb 2025 11:27:11 +0300 Subject: [PATCH] chore(app): creating aptos client according to wallet wip Signed-off-by: Kaan Caglan --- typescript-sdk/src/aptos/client.ts | 150 ++++++++++++++++++++++------- 1 file changed, 116 insertions(+), 34 deletions(-) diff --git a/typescript-sdk/src/aptos/client.ts b/typescript-sdk/src/aptos/client.ts index 26a1dbc027..73b26450ee 100644 --- a/typescript-sdk/src/aptos/client.ts +++ b/typescript-sdk/src/aptos/client.ts @@ -1,20 +1,22 @@ import { type AptosAccount, - // (These below helpers remain as before) waitForTransactionReceipt, type AptosPublicAccountInfo } from "./transfer.ts" -import { err, type Result } from "neverthrow" +import { ok, err, type Result } from "neverthrow" import { Aptos, Network, AptosConfig, AccountAddress, MoveVector } from "@aptos-labs/ts-sdk" import { createClient, fallback, type HttpTransport } from "viem" import type { AptosBrowserWallet, AuthAccess } from "./wallet.ts" +// Define a unified signer type that always includes an accountAddress. +export type AptosSigner = AptosAccount | (AptosBrowserWallet & { accountAddress: string }) + export type { AptosAccount, AptosBrowserWallet } export const aptosChainId = [ - "2", // aptos testnet + "2", // aptos testnet "177", // movement porto - "250" // movement bardock + "250" // movement bardock ] as const export type AptosChainId = `${(typeof aptosChainId)[number]}` @@ -31,23 +33,25 @@ export type AptosClientParameters = { | { account: AptosAccount; transport: HttpTransport } | { account?: AptosPublicAccountInfo; transport: AptosWindowTransport } ) +export type WalletSigner = AptosBrowserWallet & { accountAddress: string }; /** * Overloads for retrieving an Aptos client. */ async function getAptosClient( parameters: AptosClientParameters & { authAccess: "key" } -): Promise<{ authAccess: "key"; aptos: Aptos; signer: AptosAccount }> +): Promise<{ authAccess: "key"; aptos: Aptos; signer: AptosSigner; transport: HttpTransport }>; + +async function getAptosClient( + parameters: AptosClientParameters & { authAccess: "wallet" } +): Promise<{ authAccess: "wallet"; aptos: Aptos; signer: AptosSigner; transport: AptosWindowTransport }>; -// async function getAptosClient( -// parameters: AptosClientParameters & { authAccess: "wallet" } -// ): Promise<{ authAccess: "wallet"; aptos: Aptos; signer: AptosBrowserWallet }> async function getAptosClient( parameters: AptosClientParameters & { authAccess: AuthAccess } ): Promise< - | { authAccess: "key"; aptos: Aptos; signer: AptosAccount } - | { authAccess: "wallet"; aptos: Aptos; signer: AptosBrowserWallet } + | { authAccess: "key"; aptos: Aptos; signer: AptosSigner; transport: HttpTransport } + | { authAccess: "wallet"; aptos: Aptos; signer: AptosSigner; transport: AptosWindowTransport } > { if (parameters.authAccess === "key") { if (typeof parameters.transport !== "function") { @@ -62,7 +66,8 @@ async function getAptosClient( return { authAccess: "key", aptos: new Aptos(config), - signer: parameters.account as AptosAccount + signer: parameters.account as AptosAccount, // AptosAccount is assumed to have accountAddress + transport: parameters.transport } } @@ -71,20 +76,36 @@ async function getAptosClient( throw new Error("Invalid Aptos transport") } const networkInfo = await parameters.transport.getNetwork() - const network = networkInfo.name.toLowerCase() === "mainnet" ? Network.MAINNET : Network.TESTNET + const network = + networkInfo.name.toLowerCase() === "mainnet" ? Network.MAINNET : Network.TESTNET const config = new AptosConfig({ fullnode: networkInfo.url, network }) + + // Get the connected account + const account = await parameters.transport.getAccount?.() || + { address: "" } + if (!account.address) { + throw new Error("No account address found from the wallet") + } + + // Create a signer by merging the wallet’s methods with the account address. + const signer = Object.assign({}, parameters.transport, { + accountAddress: account.address + }) as unknown as AptosAccount // <== Force-cast to AptosAccount + return { authAccess: "wallet", aptos: new Aptos(config), - signer: parameters.transport as AptosBrowserWallet + signer: signer, + transport: parameters.transport } } + + throw new Error("Invalid Aptos transport") } /** - * New unified transfer parameters for Aptos, - * matching the Cosmos & EVM clients. + * New unified transfer parameters for Aptos, matching the Cosmos & EVM clients. */ export interface TransferAssetParameters { baseAmount: bigint @@ -102,17 +123,22 @@ export interface TransferAssetParameters { */ export const createAptosClient = (clientParameters: AptosClientParameters) => { return createClient({ transport: fallback([]) }) - .extend(_ => ({ - // A helper to get the underlying Aptos client. - // We default to "key" if an account was provided. - getAptosClient: async () => await getAptosClient({ ...clientParameters, authAccess: "key" }) - // clientParameters.account - // ? await getAptosClient({ ...clientParameters, authAccess: "key" }) - // : await getAptosClient({ ...clientParameters, authAccess: "wallet" }) + .extend(() => ({ + getAptosClient: async () => { + console.info("create aptos client params:", clientParameters) + // Use the transport type to determine which client to create. + if (typeof clientParameters.transport === "function") { + console.info("returning key-based client") + return await getAptosClient({ ...clientParameters, authAccess: "key" }) + } else { + console.info("returning wallet-based client") + return await getAptosClient({ ...clientParameters, authAccess: "wallet" }) + } + } })) .extend(client => ({ waitForTransactionReceipt: async ({ hash }: { hash: string }) => { - const { aptos, signer } = await client.getAptosClient() + const { aptos } = await client.getAptosClient() return await waitForTransactionReceipt({ aptos, hash }) }, @@ -129,15 +155,22 @@ export const createAptosClient = (clientParameters: AptosClientParameters) => { sourceChannelId, ucs03address }: TransferAssetParameters): Promise> => { - const { aptos, signer } = await client.getAptosClient() - - const baseTokenHex = baseToken.startsWith("0x") ? baseToken.slice(2) : baseToken // Remove "0x" if it exists - // let my_addr = AccountAddress.fromHex(baseToken) + const { aptos, signer, authAccess, transport } = await client.getAptosClient(); + console.info("aptos", aptos) + console.info("signer", signer) + console.info("baseAmount", baseAmount) + console.info("baseToken", baseToken) + console.info("quoteAmount", quoteAmount) + console.info("quoteToken", quoteToken) + console.info("receiver", receiver) + console.info("sourceChannelId", sourceChannelId) + console.info("ucs03address", ucs03address) + const baseTokenHex = baseToken.startsWith("0x") ? baseToken.slice(2) : baseToken const quoteTokenVec = MoveVector.U8(quoteToken) const receiverVec = MoveVector.U8(receiver) - const rawSalt = new Uint8Array(32) + const rawSalt = new Uint8Array(14) crypto.getRandomValues(rawSalt) const salt = MoveVector.U8(rawSalt) @@ -160,16 +193,65 @@ export const createAptosClient = (clientParameters: AptosClientParameters) => { } }) + // Transform the payload to match AptosWalletTransactionPayload + console.info("before salthex") + const saltHex = toHex(rawSalt); + console.info("saltHex is:", saltHex) + + const walletPayload = { + function: `${ucs03address}::ibc_app::transfer`, + type_arguments: [], + arguments: [ + sourceChannelId.toString(), + "union14qemq0vw6y3gc3u3e0aty2e764u4gs5lnxk4rv", + baseToken, + baseAmount.toString(), + "muno", + quoteAmount.toString(), + 18446744073709551615n.toString(), + 18446744073709551615n.toString(), + saltHex + ] + }; + console.info("walletPayload is:", walletPayload) + // return err(new Error("not implemented")) + try { - const txn = await aptos.signAndSubmitTransaction({ - signer: signer, - transaction: payload - }) - const receipt = await waitForTransactionReceipt({ aptos, hash: txn.hash }) - return receipt + let txn; + if (authAccess === "key") { + console.info("key-based flow") + console.info("signer:", signer) + console.info("payload:", payload) + // Key-based flow using the full AptosAccount + txn = await aptos.signAndSubmitTransaction({ + signer: signer as AptosAccount, + transaction: payload + }); + console.info("txn:", txn) + const receipt = await waitForTransactionReceipt({ aptos, hash: txn.hash }); + return receipt; + } else { + + console.info("wallet-based flow"); + console.info("signer:", signer); + console.info("payload:", walletPayload); + try { + const signedTxn = await transport.signAndSubmitTransaction({ payload: walletPayload }); + console.info("signedTxn:", signedTxn); + return ok(signedTxn.hash); // Wrap the string in a successful Result + } catch (error) { + return err(new Error("Transaction signing failed")); + } + } } catch (error) { + console.info("error is:", error) return err(new Error("failed to execute aptos call", { cause: error as Error })) } } })) } +function toHex(uint8array: Uint8Array): string { + return "0x" + Array.from(uint8array) + .map(b => b.toString(16).padStart(2, "0")) + .join(""); +} \ No newline at end of file