From 5977223cc3fdb68c20e87a7d7b9a3e03ef575d1f Mon Sep 17 00:00:00 2001 From: Hoang Phuong Nam Date: Thu, 11 Apr 2024 01:34:43 +0700 Subject: [PATCH] fix: bug related shielded action for transfer --- .../src/background/approvals/handler.ts | 13 +- .../src/background/approvals/service.ts | 20 ++- .../src/background/approvals/types.ts | 1 + .../src/background/chains/service.ts | 4 +- .../src/background/keyring/keyring.ts | 114 +++++++++++++++--- .../src/background/keyring/service.ts | 113 +++-------------- apps/extension/src/provider/Namada.ts | 8 +- apps/extension/src/provider/Signer.ts | 24 +++- apps/extension/src/provider/messages.ts | 3 +- .../src/App/Token/IBCTransfer/IBCTransfer.tsx | 22 ++-- .../src/App/Token/TokenSend/TokenSendForm.tsx | 8 +- apps/namada-interface/src/slices/accounts.ts | 34 +++++- apps/namada-interface/src/store/mocks.ts | 1 + packages/chains/src/chains/osmosis.ts | 34 ++++++ packages/chains/src/index.ts | 2 + packages/chains/src/types.ts | 1 + packages/integrations/src/Namada.ts | 6 +- packages/shared/lib/src/query.rs | 40 +++++- packages/shared/lib/src/sdk/masp.rs | 2 + packages/shared/lib/src/sdk/mod.rs | 39 ++++-- packages/shared/lib/src/sdk/tx.rs | 24 +++- packages/types/src/account.ts | 1 + packages/types/src/chain.ts | 2 +- packages/types/src/namada.ts | 1 + packages/types/src/signer.ts | 6 +- 25 files changed, 353 insertions(+), 170 deletions(-) create mode 100644 packages/chains/src/chains/osmosis.ts diff --git a/apps/extension/src/background/approvals/handler.ts b/apps/extension/src/background/approvals/handler.ts index 4c5604ba5..76f34d5ee 100644 --- a/apps/extension/src/background/approvals/handler.ts +++ b/apps/extension/src/background/approvals/handler.ts @@ -72,8 +72,17 @@ export const getHandler: (service: ApprovalsService) => Handler = (service) => { const handleApproveTxMsg: ( service: ApprovalsService ) => InternalHandler = (service) => { - return async (_, { txType, specificMsg, txMsg, accountType }) => { - return await service.approveTx(txType, specificMsg, txMsg, accountType); + return async ( + _, + { txType, specificMsg, txMsg, accountType, transparentAddress } + ) => { + return await service.approveTx( + txType, + specificMsg, + txMsg, + accountType, + transparentAddress + ); }; }; diff --git a/apps/extension/src/background/approvals/service.ts b/apps/extension/src/background/approvals/service.ts index 19677f740..bfbd9058d 100644 --- a/apps/extension/src/background/approvals/service.ts +++ b/apps/extension/src/background/approvals/service.ts @@ -124,10 +124,16 @@ export class ApprovalsService { txType: SupportedTx, txMsg: string, specificMsg: string, - type: AccountType + type: AccountType, + transparentAddress?: string ): Promise { const msgId = uuid(); - await this.txStore.set(msgId, { txType, txMsg, specificMsg }); + await this.txStore.set(msgId, { + txType, + txMsg, + specificMsg, + transparentAddress, + }); // Decode tx details and launch approval screen const txMsgBuffer = Buffer.from(fromBase64(txMsg)); @@ -317,7 +323,7 @@ export class ApprovalsService { throw new Error("Pending tx not found!"); } - const { txType, specificMsg, txMsg } = tx; + const { txType, specificMsg, txMsg, transparentAddress } = tx; const submitFn = txType === TxType.Bond @@ -336,7 +342,13 @@ export class ApprovalsService { ? this.keyRingService.submitVoteProposal : assertNever(txType); - await submitFn.call(this.keyRingService, specificMsg, txMsg, msgId); + await submitFn.call( + this.keyRingService, + specificMsg, + txMsg, + msgId, + transparentAddress + ); return await this._clearPendingTx(msgId); } diff --git a/apps/extension/src/background/approvals/types.ts b/apps/extension/src/background/approvals/types.ts index fffae64f7..6673a3087 100644 --- a/apps/extension/src/background/approvals/types.ts +++ b/apps/extension/src/background/approvals/types.ts @@ -6,4 +6,5 @@ export type TxStore = { txType: SupportedTx; txMsg: string; specificMsg: string; + transparentAddress?: string; }; diff --git a/apps/extension/src/background/chains/service.ts b/apps/extension/src/background/chains/service.ts index c4b3f4423..95544d07b 100644 --- a/apps/extension/src/background/chains/service.ts +++ b/apps/extension/src/background/chains/service.ts @@ -26,7 +26,7 @@ export class ChainsService { console.warn(`Chain is unreachable: ${e}`); } - await this.localStorage.setChain(chain); + await this.localStorage.setChain(chain as any); return chain; } @@ -59,7 +59,7 @@ export class ChainsService { chainId, rpc: url, }; - await this.localStorage.setChain(await this._queryNativeToken(chain)); + await this.localStorage.setChain(await this._queryNativeToken(chain) as any); await this.broadcaster.updateNetwork(); } } diff --git a/apps/extension/src/background/keyring/keyring.ts b/apps/extension/src/background/keyring/keyring.ts index 8e8b2edb4..b886b3a9e 100644 --- a/apps/extension/src/background/keyring/keyring.ts +++ b/apps/extension/src/background/keyring/keyring.ts @@ -51,6 +51,7 @@ import { UtilityStore, } from "./types"; +import { fromBase64 } from "@cosmjs/encoding"; import { SdkService } from "background/sdk"; import { VaultService } from "background/vault"; import { KeyStore, VaultStorage } from "storage"; @@ -768,14 +769,58 @@ export class KeyRing { } } + async submit( + transferMsg: string, + txMsg: string, + msgId: string, + signingKey: SigningKey + ) { + const sdk = await this.sdkService.getSdk(); + const data = { + transferMsg, + txMsg, + msgId, + signingKey, + }; + console.log("data", data); + try { + const { + signingKey: { privateKey, xsk }, + } = data; + let txMsg = fromBase64(data.txMsg); + await sdk.load_masp_params(); + let finalPrivate; + + if (privateKey) { + await sdk.reveal_pk(privateKey, txMsg); + finalPrivate = privateKey; + } + + const builtTx = await sdk.build_transfer( + fromBase64(data.transferMsg), + txMsg, + xsk + ); + const txBytes = await sdk.sign_tx(builtTx, txMsg, finalPrivate); + return await sdk.process_tx(txBytes, txMsg); + } catch (error) { + console.error("error build", error); + return ""; + } + } + async submitTransfer( - transferMsg: Uint8Array, - submit: (signingKey: SigningKey) => Promise + transferMsgBase64: Uint8Array, + transferMsg: string, + txMsg: string, + msgId: string, + transparentAddress?: string ): Promise { await this.vaultService.assertIsUnlocked(); - - // We need to get the source address to find either the private key or spending key - const { source } = deserialize(Buffer.from(transferMsg), TransferMsgValue); + const { source } = deserialize( + Buffer.from(transferMsgBase64), + TransferMsgValue + ); const account = await this.vaultStorage.findOneOrFail( KeyStore, @@ -786,25 +831,31 @@ export class KeyRing { await this.vaultService.reveal( account.sensitive ); - + console.log("sensitiveProps", sensitiveProps); if (!sensitiveProps) { throw new Error("Error decrypting AccountStore data"); } - - if (account.public.type === AccountType.ShieldedKeys) { + const isShielded = account.public.type === AccountType.ShieldedKeys; + if (isShielded && transparentAddress) { const xsk = JSON.parse(sensitiveProps.text).spendingKey; + const privateKey = await this.getSigningKey(transparentAddress); - await submit({ + const key = { xsk, - }); + privateKey, + }; + await this.submit(transferMsg, txMsg, msgId, key); } else { - await submit({ privateKey: await this.getSigningKey(source) }); + await this.submit(transferMsg, txMsg, msgId, { + privateKey: await this.getSigningKey(source), + }); } } async submitIbcTransfer( ibcTransferMsg: Uint8Array, - txMsg: Uint8Array + txMsg: Uint8Array, + transparentAddress?: string ): Promise { await this.vaultService.assertIsUnlocked(); const sdk = await this.sdkService.getSdk(); @@ -813,14 +864,43 @@ export class KeyRing { Buffer.from(ibcTransferMsg), IbcTransferMsgValue ); - const signingKey = await this.getSigningKey(source); - - await sdk.reveal_pk(signingKey, txMsg); + console.log("source", source); + console.log("transparentAddress", transparentAddress); + const account = await this.vaultStorage.findOneOrFail( + KeyStore, + "address", + source + ); + console.log("account", account); + let privateKey; + let xsk; + await sdk.load_masp_params(); + const isShieldedTransaction = + account.public.type === AccountType.ShieldedKeys; + if (isShieldedTransaction && transparentAddress) { + const sensitiveProps = + await this.vaultService.reveal( + account.sensitive + ); + console.log("sensitiveProps", sensitiveProps); + if (!sensitiveProps) { + throw new Error("Error decrypting AccountStore data"); + } - const builtTx = await sdk.build_ibc_transfer(ibcTransferMsg, txMsg); - const txBytes = await sdk.sign_tx(builtTx, txMsg, signingKey); + xsk = JSON.parse(sensitiveProps.text).spendingKey; + privateKey = await this.getSigningKey(transparentAddress); + } else { + privateKey = await this.getSigningKey(source); + await sdk.reveal_pk(privateKey, txMsg); + } + console.log("Before byte"); + const builtTx = await sdk.build_ibc_transfer(ibcTransferMsg, txMsg, xsk); + console.log("builtTx", builtTx); + const txBytes = await sdk.sign_tx(builtTx, txMsg, privateKey); + console.log("txBytes", txBytes); await sdk.process_tx(txBytes, txMsg); } catch (e) { + console.log("submitIbcTransfer error", e); throw new Error(`Could not submit ibc transfer tx: ${e}`); } } diff --git a/apps/extension/src/background/keyring/service.ts b/apps/extension/src/background/keyring/service.ts index cbbc20c89..ab3754161 100644 --- a/apps/extension/src/background/keyring/service.ts +++ b/apps/extension/src/background/keyring/service.ts @@ -12,20 +12,9 @@ import { import { Result, truncateInMiddle } from "@namada/utils"; import { ChainsService } from "background/chains"; -import { - createOffscreenWithTxWorker, - hasOffscreenDocument, - OFFSCREEN_TARGET, - SUBMIT_TRANSFER_MSG_TYPE, -} from "background/offscreen"; import { SdkService } from "background/sdk/service"; import { VaultService } from "background/vault"; -import { init as initSubmitTransferWebWorker } from "background/web-workers"; -import { - ExtensionBroadcaster, - ExtensionRequester, - getNamadaRouterId, -} from "extension"; +import { ExtensionBroadcaster, ExtensionRequester } from "extension"; import { KeyStore, LocalStorage, VaultStorage } from "storage"; import { KeyRing } from "./keyring"; import { @@ -35,7 +24,6 @@ import { DeleteAccountError, MnemonicValidationResponse, ParentAccount, - SigningKey, UtilityStore, } from "./types"; import { syncTabs, updateTabStorage } from "./utils"; @@ -263,61 +251,6 @@ export class KeyRingService { } } - private async submitTransferChrome( - transferMsg: string, - txMsg: string, - msgId: string, - signingKey: SigningKey - ): Promise { - const offscreenDocumentPath = "offscreen.html"; - const routerId = await getNamadaRouterId(this.localStorage); - const rpc = await this.sdkService.getRpc(); - const { - currency: { address: nativeToken = tokenAddress }, - } = await this.chainsService.getChain(); - - if (!(await hasOffscreenDocument(offscreenDocumentPath))) { - await createOffscreenWithTxWorker(offscreenDocumentPath); - } - - const result = await chrome.runtime.sendMessage({ - type: SUBMIT_TRANSFER_MSG_TYPE, - target: OFFSCREEN_TARGET, - routerId, - data: { transferMsg, txMsg, msgId, signingKey, rpc, nativeToken }, - }); - - if (result?.error) { - const error = new Error(result.error?.message || "Error in web worker"); - error.stack = result.error.stack; - throw error; - } - } - - private async submitTransferFirefox( - transferMsg: string, - txMsg: string, - msgId: string, - signingKey: SigningKey - ): Promise { - const rpc = await this.sdkService.getRpc(); - const { - currency: { address: nativeToken = tokenAddress }, - } = await this.chainsService.getChain(); - - initSubmitTransferWebWorker( - { - transferMsg, - txMsg, - msgId, - signingKey, - rpc, - nativeToken, - }, - this.handleTransferCompleted.bind(this) - ); - } - /** * Submits a transfer transaction to the chain. * Handles both Shielded and Transparent transfers. @@ -331,30 +264,21 @@ export class KeyRingService { async submitTransfer( transferMsg: string, txMsg: string, - msgId: string + msgId: string, + transparentAddress?: string ): Promise { - // Passing submit handler simplifies worker code when using Firefox - const submit = async (signingKey: SigningKey): Promise => { - const { TARGET } = process.env; - if (TARGET === "chrome") { - await this.submitTransferChrome(transferMsg, txMsg, msgId, signingKey); - } else if (TARGET === "firefox") { - await this.submitTransferFirefox(transferMsg, txMsg, msgId, signingKey); - } else { - console.warn( - "Submitting transfers is not supported with your browser." - ); - } - }; - await this.broadcaster.startTx(msgId, TxType.Transfer); try { await this._keyRing.submitTransfer( fromBase64(transferMsg), - submit.bind(this) + transferMsg, + txMsg, + msgId, + transparentAddress ); - await this.broadcaster.updateBalance(); + this.broadcaster.completeTx(msgId, TxType.IBCTransfer, true); + this.broadcaster.updateBalance(); } catch (e) { console.warn(e); throw new Error(`Unable to submit the transfer! ${e}`); @@ -364,25 +288,22 @@ export class KeyRingService { async submitIbcTransfer( ibcTransferMsg: string, txMsg: string, - msgId: string + msgId: string, + transparentAddress?: string ): Promise { await this.broadcaster.startTx(msgId, TxType.IBCTransfer); try { await this._keyRing.submitIbcTransfer( fromBase64(ibcTransferMsg), - fromBase64(txMsg) + fromBase64(txMsg), + transparentAddress ); - await this.broadcaster.completeTx(msgId, TxType.IBCTransfer, true); - await this.broadcaster.updateBalance(); + this.broadcaster.completeTx(msgId, TxType.IBCTransfer, true); + this.broadcaster.updateBalance(); } catch (e) { console.warn(e); - await this.broadcaster.completeTx( - msgId, - TxType.IBCTransfer, - false, - `${e}` - ); + this.broadcaster.completeTx(msgId, TxType.IBCTransfer, false, `${e}`); throw new Error(`Unable to encode IBC transfer! ${e}`); } } @@ -395,7 +316,7 @@ export class KeyRingService { await this.broadcaster.startTx(msgId, TxType.EthBridgeTransfer); try { - await this._keyRing.submitEthBridgeTransfer( + this._keyRing.submitEthBridgeTransfer( fromBase64(ethBridgeTransferMsg), fromBase64(txMsg) ); diff --git a/apps/extension/src/provider/Namada.ts b/apps/extension/src/provider/Namada.ts index 7344ee527..065da048e 100644 --- a/apps/extension/src/provider/Namada.ts +++ b/apps/extension/src/provider/Namada.ts @@ -136,7 +136,13 @@ export class Namada implements INamada { public async submitTx(props: TxMsgProps): Promise { return await this.requester?.sendMessage( Ports.Background, - new ApproveTxMsg(props.txType, props.specificMsg, props.txMsg, props.type) + new ApproveTxMsg( + props.txType, + props.specificMsg, + props.txMsg, + props.type, + props.transparentAddress + ) ); } diff --git a/apps/extension/src/provider/Signer.ts b/apps/extension/src/provider/Signer.ts index 6c437ca45..03c7114f6 100644 --- a/apps/extension/src/provider/Signer.ts +++ b/apps/extension/src/provider/Signer.ts @@ -82,7 +82,8 @@ export class Signer implements ISigner { constructor: new (args: Args) => T, args: Args, txArgs: TxProps, - type: AccountType + type: AccountType, + transparentAddress?: string ): Promise { const msgValue = new constructor(args); const msg = new Message(); @@ -97,6 +98,7 @@ export class Signer implements ISigner { specificMsg: toBase64(encoded), txMsg: toBase64(txEncoded), type, + transparentAddress, }); } @@ -168,9 +170,17 @@ export class Signer implements ISigner { public async submitTransfer( args: TransferProps, txArgs: TxProps, - type: AccountType + type: AccountType, + transparentAddress?: string ): Promise { - return this.submitTx(TxType.Transfer, TransferMsgValue, args, txArgs, type); + return this.submitTx( + TxType.Transfer, + TransferMsgValue, + args, + txArgs, + type, + transparentAddress + ); } /** @@ -179,14 +189,16 @@ export class Signer implements ISigner { public async submitIbcTransfer( args: IbcTransferProps, txArgs: TxProps, - type: AccountType + type: AccountType, + transparentAddress?: string ): Promise { - return this.submitTx( + return await this.submitTx( TxType.IBCTransfer, IbcTransferMsgValue, args, txArgs, - type + type, + transparentAddress, ); } diff --git a/apps/extension/src/provider/messages.ts b/apps/extension/src/provider/messages.ts index f43f15d52..9f3e5eee4 100644 --- a/apps/extension/src/provider/messages.ts +++ b/apps/extension/src/provider/messages.ts @@ -221,7 +221,8 @@ export class ApproveTxMsg extends Message { public readonly txType: SupportedTx, public readonly txMsg: string, public readonly specificMsg: string, - public readonly accountType: AccountType + public readonly accountType: AccountType, + public readonly transparentAddress?: string ) { super(); } diff --git a/apps/namada-interface/src/App/Token/IBCTransfer/IBCTransfer.tsx b/apps/namada-interface/src/App/Token/IBCTransfer/IBCTransfer.tsx index cf6d6abd0..f87d361bf 100644 --- a/apps/namada-interface/src/App/Token/IBCTransfer/IBCTransfer.tsx +++ b/apps/namada-interface/src/App/Token/IBCTransfer/IBCTransfer.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import BigNumber from "bignumber.js"; import { chains } from "@namada/chains"; @@ -53,12 +53,13 @@ export const submitIbcTransfer = async ( ibcArgs: TxIbcTransferArgs ): Promise => { const { - account: { address, chainId, publicKey, type }, + account: { address, publicKey, type,transparentAddress }, token, target, amount, portId, channelId, + chainId, nativeToken, } = ibcArgs; const integration = getIntegration(chainKey); @@ -81,7 +82,8 @@ export const submitIbcTransfer = async ( chainId, }, }, - type + type, + transparentAddress ); }; @@ -158,7 +160,7 @@ const IBCTransfer = (): JSX.Element => { })); const accounts = Object.values(derived[sourceChain.id]); - const sourceAccounts = accounts.filter(({ details }) => !details.isShielded); + const sourceAccounts = accounts.filter(({ details }) => details); const tokenData: Option[] = sourceAccounts.flatMap( ({ details, balance }) => { @@ -175,9 +177,11 @@ const IBCTransfer = (): JSX.Element => { } ); + const firstTimeSet = useRef(false); useEffect(() => { - if (sourceAccounts.length > 0) { + if (sourceAccounts.length > 0 && !firstTimeSet.current) { setSourceAccount(sourceAccounts[0]); + firstTimeSet.current = true; } }, [sourceAccounts]); @@ -267,22 +271,22 @@ const IBCTransfer = (): JSX.Element => { }; const handleSubmit = (): void => { - setError(undefined) + setError(undefined); const tokens = sourceChain.id === "namada" ? Tokens : CosmosTokens; if (sourceAccount && token) { submitIbcTransfer(sourceChain.id, { account: sourceAccount.details, token: tokens[token as TokenType & CosmosTokenType], amount, - chainId: sourceChainId, + chainId: chain.chainId, target: recipient, channelId: selectedChannelId, portId, nativeToken: chain.currency.address || tokenAddress, memo, }).catch((e) => { - setError(`${e}`) - }) + setError(`${e}`); + }); } }; diff --git a/apps/namada-interface/src/App/Token/TokenSend/TokenSendForm.tsx b/apps/namada-interface/src/App/Token/TokenSend/TokenSendForm.tsx index 05e459e4d..aa4a83604 100644 --- a/apps/namada-interface/src/App/Token/TokenSend/TokenSendForm.tsx +++ b/apps/namada-interface/src/App/Token/TokenSend/TokenSendForm.tsx @@ -29,7 +29,7 @@ export const submitTransferTransaction = async ( txTransferArgs: TxTransferArgs ): Promise => { const { - account: { address, publicKey, type }, + account: { address, publicKey, type,transparentAddress }, amount, chainId, target, @@ -62,13 +62,13 @@ export const submitTransferTransaction = async ( feeAmount, gasLimit, chainId, - publicKey: publicKey, + publicKey, signer: undefined, disposableSigningKey, memo, }; - await signer.submitTransfer(transferArgs, txArgs, type); + await signer.submitTransfer(transferArgs, txArgs, type,transparentAddress); }; type Props = { @@ -224,7 +224,7 @@ const TokenSendForm = ({ gasLimit, disposableSigningKey: isShieldedSource, memo, - nativeToken: chain.currency.address || tokenAddress, + nativeToken: chain.currency.address || tokenAddress }); } }; diff --git a/apps/namada-interface/src/slices/accounts.ts b/apps/namada-interface/src/slices/accounts.ts index 9e33f404e..146d629d7 100644 --- a/apps/namada-interface/src/slices/accounts.ts +++ b/apps/namada-interface/src/slices/accounts.ts @@ -31,6 +31,7 @@ const INITIAL_STATE = { namada: {}, cosmos: {}, ethereum: {}, + osmosis: {} }, }; @@ -39,6 +40,30 @@ enum AccountsThunkActions { FetchBalance = "fetchBalance", } +function convertAddresses(accounts: AccountDetails[]): AccountDetails[] { + if (accounts?.length < 2) return []; + const mnemonicMap: Record = {}; + + // Populate the map with mnemonic accounts + accounts.forEach((obj) => { + if (obj.type === "mnemonic") { + mnemonicMap[obj.alias] = obj; + } + }); + + accounts.forEach((obj) => { + if (obj.type === "shielded-keys") { + const mnemonic = mnemonicMap[obj.alias]; + if (mnemonic) { + obj.publicKey = mnemonic.publicKey; + obj.transparentAddress = mnemonic.address; + } + } + }); + + return accounts; +} + const initialState: AccountsState = INITIAL_STATE; export const fetchBalances = createAsyncThunk( @@ -86,14 +111,17 @@ const accountsSlice = createSlice({ initialState, reducers: { addAccounts: (state, action: PayloadAction) => { - const accounts = action.payload; + const rawAccounts = action.payload; - const id = accounts[0]?.chainKey || chains.namada.id; + const id = rawAccounts[0]?.chainKey || chains.namada.id; // Remove old accounts under this chain config id if present: if (state.derived[id]) { state.derived[id] = {}; } + const cloneAccounts = JSON.parse(JSON.stringify(rawAccounts)) + + const accounts = convertAddresses(cloneAccounts) accounts.forEach((account) => { const { @@ -104,6 +132,7 @@ const accountsSlice = createSlice({ type, publicKey, chainKey, + transparentAddress, } = account; const currencySymbol = chains.namada.currency.symbol; if (!state.derived[id]) { @@ -119,6 +148,7 @@ const accountsSlice = createSlice({ publicKey, isShielded, chainKey, + transparentAddress }, balance: { [currencySymbol]: new BigNumber(0), diff --git a/apps/namada-interface/src/store/mocks.ts b/apps/namada-interface/src/store/mocks.ts index a8722d5c8..257e833a8 100644 --- a/apps/namada-interface/src/store/mocks.ts +++ b/apps/namada-interface/src/store/mocks.ts @@ -27,6 +27,7 @@ export const mockAppState: RootState = { }, cosmos: {}, ethereum: {}, + osmosis: {}, }, }, chain: { config: chains.namada }, diff --git a/packages/chains/src/chains/osmosis.ts b/packages/chains/src/chains/osmosis.ts new file mode 100644 index 000000000..81b05ca32 --- /dev/null +++ b/packages/chains/src/chains/osmosis.ts @@ -0,0 +1,34 @@ +import { BridgeType, Chain, Extensions } from "@namada/types"; +import { ProxyMappings } from "../types"; + +const DEFAULT_ALIAS = "Osmosis"; +const DEFAULT_CHAIN_ID = "osmosis-5"; +const DEFAULT_RPC = "https://rpc.osmotest5.osmosis.zone"; + +const rpc = DEFAULT_RPC; +const chainId = DEFAULT_CHAIN_ID; +const alias = DEFAULT_ALIAS; +const isProxied = false; + +const cosmos: Chain = { + id: "osmosis", + alias, + bech32Prefix: "osmosis", + bip44: { + coinType: 118, + }, + bridgeType: [BridgeType.IBC], + rpc: isProxied ? ProxyMappings["osmosis"] : rpc, + chainId, + currency: { + token: "osmo", + symbol: "OSMO", + gasPriceStep: { low: 0.01, average: 0.025, high: 0.03 }, + }, + extension: Extensions["keplr"], + ibc: { + portId: "transfer", + }, +}; + +export default cosmos; diff --git a/packages/chains/src/index.ts b/packages/chains/src/index.ts index 782363ef9..64193bace 100644 --- a/packages/chains/src/index.ts +++ b/packages/chains/src/index.ts @@ -2,11 +2,13 @@ import { Chain, ChainKey } from "@namada/types"; import cosmos from "./chains/cosmos"; import ethereum from "./chains/ethereum"; import namada from "./chains/namada"; +import osmosis from "./chains/osmosis"; export const chains: Record = { cosmos, namada, ethereum, + osmosis }; export * from "./types"; diff --git a/packages/chains/src/types.ts b/packages/chains/src/types.ts index 79d207f81..bfeb8351d 100644 --- a/packages/chains/src/types.ts +++ b/packages/chains/src/types.ts @@ -7,4 +7,5 @@ export const ProxyMappings: Record = { namada: getProxyURL(8010), cosmos: getProxyURL(8011), ethereum: getProxyURL(8012), + osmosis: getProxyURL(8013), }; diff --git a/packages/integrations/src/Namada.ts b/packages/integrations/src/Namada.ts index c55a77602..853e113b0 100644 --- a/packages/integrations/src/Namada.ts +++ b/packages/integrations/src/Namada.ts @@ -61,14 +61,16 @@ export default class Namada implements Integration { public async submitBridgeTransfer( props: BridgeProps, - type: AccountType + type: AccountType, + transparentAddress?: string ): Promise { const signer = this._namada?.getSigner(); if (props.ibcProps) { return await signer?.submitIbcTransfer( props.ibcProps, props.txProps, - type + type, + transparentAddress ); } else if (props.bridgeProps) { return await signer?.submitEthBridgeTransfer( diff --git a/packages/shared/lib/src/query.rs b/packages/shared/lib/src/query.rs index 8eace3425..f0ee2a330 100644 --- a/packages/shared/lib/src/query.rs +++ b/packages/shared/lib/src/query.rs @@ -8,11 +8,12 @@ use namada::governance::{ProposalType, ProposalVote}; use namada::ledger::eth_bridge::bridge_pool::query_signed_bridge_pool; use namada::ledger::parameters::storage; use namada::ledger::queries::RPC; +use namada::sdk::masp_primitives::sapling::ViewingKey; +use namada::storage::BlockHeight; use namada::masp::ExtendedViewingKey; use namada::proof_of_stake::Epoch; use namada::sdk::masp::{DefaultLogger, ShieldedContext}; use namada::sdk::masp_primitives::asset_type::AssetType; -use namada::sdk::masp_primitives::sapling::ViewingKey; use namada::sdk::masp_primitives::transaction::components::ValueSum; use namada::sdk::masp_primitives::zip32::ExtendedFullViewingKey; use namada::sdk::rpc::{ @@ -25,11 +26,11 @@ use namada::uint::I256; use std::collections::{BTreeMap, HashMap, HashSet}; use std::str::FromStr; use wasm_bindgen::prelude::*; - use crate::rpc_client::HttpClient; use crate::sdk::{io::WebIo, masp}; use crate::types::query::ProposalInfo; use crate::utils::{set_panic_hook, to_js_result}; +use serde_json; #[wasm_bindgen] /// Represents an API for querying the ledger @@ -244,13 +245,11 @@ impl Query { Address::from_str(address_str).unwrap() }) .collect(); - let mut result = vec![]; for token in tokens { let balances = get_token_balance(&self.client, &token, &owner).await?; result.push((token, balances)); } - Ok(result) } @@ -296,7 +295,7 @@ impl Query { /// # Arguments /// /// * `xvk` - Extended viewing key - async fn query_shielded_balance( + async fn query_shielded_balance( &self, xvk: ExtendedViewingKey, ) -> Result, JsError> { @@ -304,7 +303,36 @@ impl Query { // We are recreating shielded context to avoid multiple mutable borrows let mut shielded: ShieldedContext = ShieldedContext::default(); - shielded.load().await?; + + let _ = shielded.load().await; + // TODO: pass supported asset types + // let native_token = "tnam1q87wtaqqtlwkw927gaff34hgda36huk0kgry692a"; // nam of luminar + // "tnam1qxgfw7myv4dh0qna4hq0xdg6lx77fzl7dcem8h7e" nam of SE + + let native_token = "tnam1qxgfw7myv4dh0qna4hq0xdg6lx77fzl7dcem8h7e"; + // let second_token = "tnam1qxgfw7myv4dh0qna4hq0xdg6lx77fzl7dcem8h7e"; + // , &Address::from_str(second_token).unwrap() + let _ = shielded + .precompute_asset_types( + &self.client, + vec![&Address::from_str(native_token).unwrap()], + ) + .await; + let _ = shielded.save().await; + // let asset = shielded.asset_types; + + shielded + .fetch( + &self.client, + &DefaultLogger::new(&WebIo), + None, + 20, + &[], + &[viewing_key], + + ) + .await?; + let _ = shielded.save().await; let epoch = query_epoch(&self.client).await?; let balance = shielded diff --git a/packages/shared/lib/src/sdk/masp.rs b/packages/shared/lib/src/sdk/masp.rs index f7f3505be..dbb6b63ff 100644 --- a/packages/shared/lib/src/sdk/masp.rs +++ b/packages/shared/lib/src/sdk/masp.rs @@ -3,8 +3,10 @@ use gloo_utils::format::JsValueSerdeExt; use namada::core::borsh::{BorshDeserialize, BorshSerialize}; use namada::sdk::masp::{ContextSyncStatus, ShieldedContext, ShieldedUtils}; use namada::sdk::masp_proofs::prover::LocalTxProver; +use namada::tendermint::node::info; use rexie::{Error, ObjectStore, Rexie, TransactionMode}; use wasm_bindgen::{JsError, JsValue}; +use crate::utils::to_js_result; use crate::utils::to_bytes; diff --git a/packages/shared/lib/src/sdk/mod.rs b/packages/shared/lib/src/sdk/mod.rs index e5e45298e..9fc4faaae 100644 --- a/packages/shared/lib/src/sdk/mod.rs +++ b/packages/shared/lib/src/sdk/mod.rs @@ -6,6 +6,14 @@ use crate::{ sdk::masp::WebShieldedUtils, utils::{set_panic_hook, to_bytes}, }; +use namada::{ + address, + key::{ + self, + common::{PublicKey, SecretKey}, + PublicKeyHash, RefTo, + }, +}; use js_sys::Uint8Array; use namada::address::Address; use namada::core::borsh::{self, BorshDeserialize}; @@ -13,7 +21,7 @@ use namada::hash::Hash; use namada::key::{common, ed25519, SigScheme}; use namada::ledger::eth_bridge::bridge_pool::build_bridge_pool_tx; use namada::masp::TransferSource; -use namada::sdk::masp::{DefaultLogger, ShieldedContext}; +use namada::sdk::masp::{DefaultLogger,ShieldedContext}; use namada::sdk::rpc::query_epoch; use namada::sdk::signing::{find_key_by_pk, SigningTxData}; use namada::sdk::tx::{ @@ -213,7 +221,7 @@ impl Sdk { .tx } TxType::IBCTransfer => { - self.build_ibc_transfer(specific_msg, tx_msg, Some(gas_payer)) + self.build_ibc_transfer(specific_msg, tx_msg, None ,Some(gas_payer)) .await? .tx } @@ -274,7 +282,6 @@ impl Sdk { ) -> Result { let mut args = tx::transfer_tx_args(transfer_msg, tx_msg, xsk.clone())?; - // TODO: this might not be needed. I will test it out in future match args.source { TransferSource::Address(_) => {} TransferSource::ExtendedSpendingKey(xsk) => { @@ -293,13 +300,10 @@ impl Sdk { } } - // It's temporary solution to add xsk to wallet as xvk is queried when unshielding - // This will change in namada in the future match xsk { Some(xsk) => self.add_spending_key(&xsk, &"temp").await, None => {} } - let (tx, signing_data, _) = build_transfer(&self.namada, &mut args).await?; Ok(BuiltTx { tx, signing_data }) @@ -309,11 +313,28 @@ impl Sdk { &mut self, ibc_transfer_msg: &[u8], tx_msg: &[u8], + xsk: Option, _gas_payer: Option, ) -> Result { - let args = tx::ibc_transfer_tx_args(ibc_transfer_msg, tx_msg)?; + let args = tx::ibc_transfer_tx_args(ibc_transfer_msg, tx_msg, xsk.clone())?; + match args.source { + TransferSource::Address(_) => {} + TransferSource::ExtendedSpendingKey(xsk) => { + self.namada + .shielded_mut() + .await + .fetch( + self.namada.client(), + &DefaultLogger::new(&WebIo), + None, + 1, + &[xsk.into()], + &[], + ) + .await?; + } + }; let (tx, signing_data, _) = build_ibc_transfer(&self.namada, &args).await?; - Ok(BuiltTx { tx, signing_data }) } @@ -408,7 +429,7 @@ impl Sdk { let built_tx = self.build_reveal_pk(tx_msg, String::from("")).await?; // Conversion from JsValue so we can use self.sign_tx let tx_bytes = - Uint8Array::new(&self.sign_tx(built_tx, tx_msg, Some(signing_key)).await?).to_vec(); + Uint8Array::new(&self.sign_tx(built_tx, tx_msg, Some(signing_key),None).await?).to_vec(); self.process_tx(&tx_bytes, tx_msg).await?; } diff --git a/packages/shared/lib/src/sdk/tx.rs b/packages/shared/lib/src/sdk/tx.rs index bc5cc2300..e055daae5 100644 --- a/packages/shared/lib/src/sdk/tx.rs +++ b/packages/shared/lib/src/sdk/tx.rs @@ -299,6 +299,7 @@ pub struct SubmitIbcTransferMsg { pub fn ibc_transfer_tx_args( ibc_transfer_msg: &[u8], tx_msg: &[u8], + xsk: Option, ) -> Result { let ibc_transfer_msg = SubmitIbcTransferMsg::try_from_slice(ibc_transfer_msg)?; let SubmitIbcTransferMsg { @@ -312,8 +313,19 @@ pub fn ibc_transfer_tx_args( timeout_sec_offset, } = ibc_transfer_msg; - let source_address = Address::from_str(&source)?; - let source = TransferSource::Address(source_address); + let source = match Address::from_str(&source) { + Ok(v) => Ok(TransferSource::Address(v)), + Err(e1) => match ExtendedSpendingKey::from_str( + &xsk.expect("Extended spending key to be present, if address type is shielded."), + ) { + Ok(v) => Ok(TransferSource::ExtendedSpendingKey(v)), + Err(e2) => Err(JsError::new(&format!( + "Can't compute the transfer source. {}, {}", + e1, e2 + ))), + }, + }?; + let token = Address::from_str(&token)?; let denom_amount = DenominatedAmount::from_str(&amount).expect("Amount to be valid."); let amount = InputAmount::Unvalidated(denom_amount); @@ -432,7 +444,7 @@ fn tx_msg_into_args(tx_msg: &[u8]) -> Result { .expect(format!("Fee amount has to be valid. Received {}", fee_amount).as_str()); let fee_input_amount = InputAmount::Unvalidated(fee_amount); - let public_key = match public_key { + let public_key = match public_key.clone() { Some(v) => { let pk = PublicKey::from_str(&v)?; Some(pk) @@ -441,7 +453,7 @@ fn tx_msg_into_args(tx_msg: &[u8]) -> Result { }; let disposable_signing_key = disposable_signing_key.unwrap_or(false); - let siginig_keys: Vec = match public_key { + let signing_keys: Vec = match public_key.clone() { Some(v) => vec![v.clone()], _ => vec![], }; @@ -455,7 +467,7 @@ fn tx_msg_into_args(tx_msg: &[u8]) -> Result { // Ledger address is not used in the SDK. // We can leave it as whatever as long as it's valid url. - let ledger_address = tendermint_rpc::Url::from_str("http://notinuse:13337").unwrap(); + let ledger_address = tendermint_rpc::Url::from_str("http://127.0.0.1:27657/").unwrap(); let memo = memo.map(|v| v.as_bytes().to_vec()); @@ -478,7 +490,7 @@ fn tx_msg_into_args(tx_msg: &[u8]) -> Result { expiration: None, chain_id: Some(ChainId(String::from(chain_id))), signatures: vec![], - signing_keys: siginig_keys, + signing_keys, tx_reveal_code_path: PathBuf::from("tx_reveal_pk.wasm"), use_device: false, password: None, diff --git a/packages/types/src/account.ts b/packages/types/src/account.ts index 350227b7e..20e1bb2af 100644 --- a/packages/types/src/account.ts +++ b/packages/types/src/account.ts @@ -36,4 +36,5 @@ export type Account = Pick< chainId: string; isShielded: boolean; chainKey: ChainKey; + transparentAddress?: string; }; diff --git a/packages/types/src/chain.ts b/packages/types/src/chain.ts index 8f73e28aa..d2d04ab60 100644 --- a/packages/types/src/chain.ts +++ b/packages/types/src/chain.ts @@ -18,7 +18,7 @@ export enum BridgeType { export type ExtensionKey = "namada" | "keplr" | "metamask"; // Define keys for supported chains -export type ChainKey = "namada" | "cosmos" | "ethereum"; +export type ChainKey = "namada" | "cosmos" | "ethereum" | "osmosis"; export type ExtensionInfo = { alias: string; diff --git a/packages/types/src/namada.ts b/packages/types/src/namada.ts index 3c23bad47..ae6969488 100644 --- a/packages/types/src/namada.ts +++ b/packages/types/src/namada.ts @@ -9,6 +9,7 @@ export type TxMsgProps = { specificMsg: string; txMsg: string; type: AccountType; + transparentAddress?: string; }; export type SignArbitraryProps = { diff --git a/packages/types/src/signer.ts b/packages/types/src/signer.ts index e6d65571d..59617d032 100644 --- a/packages/types/src/signer.ts +++ b/packages/types/src/signer.ts @@ -41,12 +41,14 @@ export interface Signer { submitTransfer( args: TransferProps, txArgs: TxProps, - type: AccountType + type: AccountType, + transparentAddress?: string ): Promise; submitIbcTransfer( args: IbcTransferProps, txArgs: TxProps, - type: AccountType + type: AccountType, + transparentAddress?: string ): Promise; submitVoteProposal( args: SubmitVoteProposalProps,