Skip to content

Commit

Permalink
feat: enable shielded transfers (#1433)
Browse files Browse the repository at this point in the history
* feat: enable shielded transfers

* fix: passing empty vks
  • Loading branch information
mateuszjasiuk authored Dec 27, 2024
1 parent 294031d commit 1ceb125
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 46 deletions.
6 changes: 3 additions & 3 deletions apps/namadillo/src/App/Masp/MaspShield.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,9 @@ export const MaspShield: React.FC = () => {
displayAmount,
rpc: txResponse.msg.payload.chain.rpcUrl,
chainId: txResponse.msg.payload.chain.chainId,
hash: txResponse.encodedTx.txs[0]?.hash,
feePaid: txResponse.encodedTx.wrapperTxProps.feeAmount,
resultTxHash: txResponse.encodedTx.txs[0]?.innerTxHashes[0],
hash: txResponse.encodedTxData.txs[0]?.hash,
feePaid: txResponse.encodedTxData.wrapperTxProps.feeAmount,
resultTxHash: txResponse.encodedTxData.txs[0]?.innerTxHashes[0],
status: "success",
createdAt: new Date(),
updatedAt: new Date(),
Expand Down
6 changes: 3 additions & 3 deletions apps/namadillo/src/App/Masp/MaspUnshield.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@ export const MaspUnshield: React.FC = () => {
displayAmount,
rpc: txResponse.msg.payload.chain.rpcUrl,
chainId: txResponse.msg.payload.chain.chainId,
hash: txResponse.encodedTx.txs[0]?.hash,
feePaid: txResponse.encodedTx.wrapperTxProps.feeAmount,
resultTxHash: txResponse.encodedTx.txs[0]?.innerTxHashes[0],
hash: txResponse.encodedTxData.txs[0]?.hash,
feePaid: txResponse.encodedTxData.wrapperTxProps.feeAmount,
resultTxHash: txResponse.encodedTxData.txs[0]?.innerTxHashes[0],
status: "success",
createdAt: new Date(),
updatedAt: new Date(),
Expand Down
36 changes: 33 additions & 3 deletions apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import { allDefaultAccountsAtom } from "atoms/accounts";
import { namadaTransparentAssetsAtom } from "atoms/balance/atoms";
import { chainParametersAtom } from "atoms/chain/atoms";
import { applicationFeaturesAtom, rpcUrlAtom } from "atoms/settings";
import {
shieldedTxAtom,
shieldTxAtom,
unshieldTxAtom,
} from "atoms/shield/atoms";
import {
createShieldedTransferAtom,
createShieldingTransferAtom,
Expand Down Expand Up @@ -55,6 +60,12 @@ export const NamadaTransfer: React.FC = () => {
const { data: availableAssetsData, isLoading: isLoadingAssets } =
useAtomValue(namadaTransparentAssetsAtom);

// TODO: remove this block once transfer logic is unified
const { mutateAsync: performUnshieldTransfer } = useAtomValue(unshieldTxAtom);
const { mutateAsync: performShieldingTransfer } = useAtomValue(shieldTxAtom);
const { mutateAsync: performShieldedTransfer } = useAtomValue(shieldedTxAtom);
// end of block

const {
transactions: myTransactions,
findByHash,
Expand All @@ -76,11 +87,12 @@ export const NamadaTransfer: React.FC = () => {
}, [availableAssetsData]);

const chainId = chainParameters.data?.chainId;
const sourceAddress = defaultAccounts.data?.find((account) =>
const account = defaultAccounts.data?.find((account) =>
shielded ?
account.type === AccountType.ShieldedKeys
: account.type !== AccountType.ShieldedKeys
)?.address;
);
const sourceAddress = account?.address;
const selectedAssetAddress = searchParams.get(params.asset) || undefined;
const selectedAsset =
selectedAssetAddress ? availableAssets?.[selectedAssetAddress] : undefined;
Expand Down Expand Up @@ -207,7 +219,25 @@ export const NamadaTransfer: React.FC = () => {
chainId,
});

const txResponse = await performTransfer({ memo });
// TODO: once transfer logic is unified, this block should be replaced by a single
// call to the performTransfer function
const shieldedTransferParams = {
sourceAddress: account!.pseudoExtendedKey || sourceAddress,
destinationAddress: target,
tokenAddress: selectedAsset.originalAddress,
amount: txAmount,
gasConfig,
};
const txResponse =
txKind === "ShieldedToTransparent" ?
await performUnshieldTransfer(shieldedTransferParams)
: txKind === "TransparentToShielded" ?
await performShieldingTransfer(shieldedTransferParams)
: txKind === "ShieldedToShielded" ?
await performShieldedTransfer(shieldedTransferParams)
: await performTransfer({ memo });
// end of block

if (txResponse) {
const txList = createTransferDataFromNamada(
txKind,
Expand Down
13 changes: 1 addition & 12 deletions apps/namadillo/src/App/WorkerTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,6 @@ export function WorkerTest(): JSX.Element {
],
});

const vks = accounts
?.filter((acc) => acc.type === "shielded-keys")
.map((a) => ({ key: a.viewingKey!, birthday: 0 }));

const disposableSigner = (await refetch()).data;

const msg: Unshield = {
Expand All @@ -163,8 +159,6 @@ export function WorkerTest(): JSX.Element {
},
unshieldingProps: [shieldingMsgValue],
chain: chain!,
vks: vks!,
indexerUrl,
},
};

Expand Down Expand Up @@ -209,10 +203,6 @@ export function WorkerTest(): JSX.Element {
],
});

const vks = accounts
?.filter((acc) => acc.type === "shielded-keys")
.map((a) => ({ key: a.viewingKey!, birthday: 0 }));

const disposableSigner = (await refetch()).data;

const msg: ShieldedTransfer = {
Expand All @@ -227,9 +217,8 @@ export function WorkerTest(): JSX.Element {
gasPrice: BigNumber(0),
gasToken: "tnam1",
},
shieldingProps: [shieldingMsgValue],
props: [shieldingMsgValue],
chain: chain!,
vks: vks!,
},
};

Expand Down
29 changes: 27 additions & 2 deletions apps/namadillo/src/atoms/shield/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { indexerUrlAtom, rpcUrlAtom } from "atoms/settings";
import { NamadaKeychain } from "hooks/useNamadaKeychain";
import { atomWithMutation } from "jotai-tanstack-query";
import {
ShieldedTransferParams,
ShieldTransferParams,
submitShieldedTx,
submitShieldTx,
submitUnshieldTx,
UnshieldTransferParams,
Expand All @@ -28,7 +30,6 @@ export const unshieldTxAtom = atomWithMutation((get) => {
const rpcUrl = get(rpcUrlAtom);
const { data: account } = get(defaultAccountAtom);
const { data: chain } = get(chainAtom);
const indexerUrl = get(indexerUrlAtom);

return {
mutationKey: ["unshield-tx"],
Expand All @@ -43,7 +44,31 @@ export const unshieldTxAtom = atomWithMutation((get) => {
rpcUrl,
account!,
chain!,
indexerUrl,
params,
disposableSigner
);
},
};
});

export const shieldedTxAtom = atomWithMutation((get) => {
const rpcUrl = get(rpcUrlAtom);
const { data: account } = get(defaultAccountAtom);
const { data: chain } = get(chainAtom);

return {
mutationKey: ["shielded-tx"],
mutationFn: async (params: ShieldedTransferParams) => {
const namada = await new NamadaKeychain().get();
const disposableSigner = await namada?.genDisposableKeypair();
if (!disposableSigner) {
throw new Error("No signer available");
}

return submitShieldedTx(
rpcUrl,
account!,
chain!,
params,
disposableSigner
);
Expand Down
89 changes: 78 additions & 11 deletions apps/namadillo/src/atoms/shield/services.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import {
Account,
GenDisposableSignerResponse,
ShieldedTransferMsgValue,
ShieldingTransferMsgValue,
UnshieldingTransferMsgValue,
} from "@namada/types";
import BigNumber from "bignumber.js";
import * as Comlink from "comlink";
import { EncodedTxData, signTx } from "lib/query";
import { Address, ChainSettings, GasConfig } from "types";
import { Shield, Unshield } from "workers/MaspTxMessages";
import { Shield, ShieldedTransfer, Unshield } from "workers/MaspTxMessages";
import {
Worker as MaspTxWorkerApi,
registerTransferHandlers as maspTxRegisterTransferHandlers,
Expand All @@ -31,6 +32,14 @@ export type UnshieldTransferParams = {
gasConfig: GasConfig;
};

export type ShieldedTransferParams = {
sourceAddress: Address;
destinationAddress: Address;
tokenAddress: Address;
amount: BigNumber;
gasConfig: GasConfig;
};

export const submitShieldTx = async (
rpcUrl: string,
account: Account,
Expand All @@ -39,7 +48,7 @@ export const submitShieldTx = async (
params: ShieldTransferParams
): Promise<{
msg: Shield;
encodedTx: EncodedTxData<ShieldingTransferMsgValue>;
encodedTxData: EncodedTxData<ShieldingTransferMsgValue>;
}> => {
const {
sourceAddress: source,
Expand Down Expand Up @@ -87,19 +96,18 @@ export const submitShieldTx = async (

worker.terminate();

return { msg, encodedTx };
return { msg, encodedTxData: encodedTx };
};

export const submitUnshieldTx = async (
rpcUrl: string,
account: Account,
chain: ChainSettings,
indexerUrl: string,
params: UnshieldTransferParams,
disposableSigner: GenDisposableSignerResponse
): Promise<{
msg: Unshield;
encodedTx: EncodedTxData<UnshieldingTransferMsgValue>;
encodedTxData: EncodedTxData<UnshieldingTransferMsgValue>;
}> => {
const {
sourceAddress: source,
Expand Down Expand Up @@ -133,24 +141,83 @@ export const submitUnshieldTx = async (
gasConfig,
unshieldingProps: [unshieldingMsgValue],
chain,
indexerUrl,
vks: [],
},
};

const { payload: encodedTx } = await unshieldWorker.unshield(msg);
const { payload: encodedTxData } = await unshieldWorker.unshield(msg);

const signedTxs = await signTx(encodedTx, disposableSigner.address);
const signedTxs = await signTx(encodedTxData, disposableSigner.address);

await unshieldWorker.broadcast({
type: "broadcast",
payload: {
encodedTx,
encodedTx: encodedTxData,
signedTxs,
},
});

worker.terminate();

return { msg, encodedTxData };
};

export const submitShieldedTx = async (
rpcUrl: string,
account: Account,
chain: ChainSettings,
params: ShieldedTransferParams,
disposableSigner: GenDisposableSignerResponse
): Promise<{
msg: ShieldedTransfer;
encodedTxData: EncodedTxData<ShieldedTransferMsgValue>;
}> => {
const {
sourceAddress: source,
destinationAddress: target,
tokenAddress: token,
amount,
gasConfig,
} = params;

maspTxRegisterTransferHandlers();
const worker = new MaspTxWorker();
const workerApi = Comlink.wrap<MaspTxWorkerApi>(worker);
await workerApi.init({
type: "init",
payload: { rpcUrl, token, maspIndexerUrl: "" },
});

const shieldedTransferMsgValue = new ShieldedTransferMsgValue({
gasSpendingKey: source,
data: [{ source, target, token, amount }],
});

const msg: ShieldedTransfer = {
type: "shielded-transfer",
payload: {
account: {
...account,
publicKey: disposableSigner.publicKey,
},
gasConfig,
props: [shieldedTransferMsgValue],
chain,
},
};

const { payload: encodedTxData } = await workerApi.shieldedTransfer(msg);

const signedTxs = await signTx(encodedTxData, disposableSigner.address);

await workerApi.broadcast({
type: "broadcast",
payload: {
encodedTx: encodedTxData,
signedTxs,
},
});

worker.terminate();

return { msg, encodedTx };
return { msg, encodedTxData };
};
10 changes: 8 additions & 2 deletions apps/namadillo/src/lib/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
TransferTransactionData,
} from "types";
import { toDisplayAmount } from "utils";
import { TransactionPair } from "./query";
import { EncodedTxData, TransactionPair } from "./query";

export const getEventAttribute = (
tx: DeliverTxResponse,
Expand Down Expand Up @@ -167,7 +167,13 @@ export const createTransferDataFromNamada = (
| TransactionPair<TransparentTransferMsgValue>
| TransactionPair<ShieldedTransferMsgValue>
| TransactionPair<ShieldingTransferMsgValue>
| TransactionPair<UnshieldingTransferMsgValue>,
| TransactionPair<UnshieldingTransferMsgValue>
// TODO: remove this block after transfer logic unification
| { encodedTxData: EncodedTxData<UnshieldingTransferMsgValue> }
| { encodedTxData: EncodedTxData<ShieldingTransferMsgValue> }
| { encodedTxData: EncodedTxData<ShieldedTransferMsgValue> },
// end of block

memo?: string
): TransferTransactionData[] => {
if (!txResponse?.encodedTxData?.txs?.length) {
Expand Down
6 changes: 1 addition & 5 deletions apps/namadillo/src/workers/MaspTxMessages.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
Account,
DatedViewingKey,
ShieldedTransferMsgValue,
ShieldingTransferMsgValue,
TxResponseMsgValue,
Expand Down Expand Up @@ -37,8 +36,6 @@ type UnshieldPayload = {
gasConfig: GasConfig;
unshieldingProps: UnshieldingTransferMsgValue[];
chain: ChainSettings;
indexerUrl: string;
vks: DatedViewingKey[];
};
export type Unshield = WebWorkerMessage<"unshield", UnshieldPayload>;
export type UnshieldDone = WebWorkerMessage<
Expand All @@ -49,9 +46,8 @@ export type UnshieldDone = WebWorkerMessage<
type ShieldedTransferPayload = {
account: Account;
gasConfig: GasConfig;
shieldingProps: ShieldedTransferMsgValue[];
props: ShieldedTransferMsgValue[];
chain: ChainSettings;
vks: DatedViewingKey[];
};
export type ShieldedTransfer = WebWorkerMessage<
"shielded-transfer",
Expand Down
Loading

1 comment on commit 1ceb125

@github-actions
Copy link

Choose a reason for hiding this comment

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

Please sign in to comment.