Skip to content

Commit

Permalink
feat/374 - Hook up IBC to Approval and Ledger flows (#375)
Browse files Browse the repository at this point in the history
* Hook up IBC to approval and ledger flows

* Remove transfers slice, update IBC and Transfer send forms

* Restore check for faucet before reveal PK

* Fix balance validation

* Clean up unneeded usage of map_err
  • Loading branch information
jurevans authored Sep 5, 2023
1 parent 4ba56fc commit 665e25f
Show file tree
Hide file tree
Showing 46 changed files with 616 additions and 1,384 deletions.
73 changes: 39 additions & 34 deletions apps/extension/src/Approvals/ApproveTx/ConfirmLedgerTx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,44 +191,49 @@ export const ConfirmLedgerTx: React.FC<Props> = ({ details }) => {
setStatus(Status.Pending);

try {
if (source && publicKey) {
// If the source is the faucet address, this is a testnet faucet transfer, and
// we do not need to reveal the public key, as it is an Established Address

if (source !== faucetAddress) {
// Query extension storage for revealed public key
const isPkRevealed = await requester
.sendMessage(Ports.Background, new QueryStoredPK(publicKey))
.catch((e) => {
throw new Error(`Requester error: ${e}`);
});

if (!isPkRevealed) {
setStatusInfo("Querying for public key on chain...");
const pk = await queryPublicKey(source);

if (pk) {
// If found on chain, but not in storage, commit to storage
await storePublicKey(publicKey);
} else {
// Submit RevealPK Tx
await revealPk(ledger, publicKey);

// Follow up with a query to ensure that PK Reveal was successful
setStatusInfo("Querying for public key status on chain...");
const wasPkRevealed = !!(await queryPublicKey(source));

if (!wasPkRevealed) {
throw new Error("Public key was not revealed!");
}

// Commit newly revealed public key to storage
await storePublicKey(publicKey);
if (!source) {
throw new Error("Source was not provided and is required!");
}

// If the source is the faucet address, this is a testnet faucet transfer, and
// we do not need to reveal the public key, as it is an Established Address
if (source !== faucetAddress) {
if (!publicKey) {
throw new Error("Public key was not provided and is required!");
}

// Query extension storage for revealed public key
const isPkRevealed = await requester
.sendMessage(Ports.Background, new QueryStoredPK(publicKey))
.catch((e) => {
throw new Error(`Requester error: ${e}`);
});

if (!isPkRevealed) {
setStatusInfo("Querying for public key on chain...");
const pk = await queryPublicKey(source);

if (pk) {
// If found on chain, but not in storage, commit to storage
await storePublicKey(publicKey);
} else {
// Submit RevealPK Tx
await revealPk(ledger, publicKey);

// Follow up with a query to ensure that PK Reveal was successful
setStatusInfo("Querying for public key status on chain...");
const wasPkRevealed = !!(await queryPublicKey(source));

if (!wasPkRevealed) {
throw new Error("Public key was not revealed!");
}

// Commit newly revealed public key to storage
await storePublicKey(publicKey);
}
}
submitTx(ledger);
}
submitTx(ledger);
} catch (e) {
await ledger.closeTransport();
setError(`${e}`);
Expand Down
8 changes: 7 additions & 1 deletion apps/extension/src/Approvals/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { TxType } from "@namada/shared";
import {
SubmitApprovedBondMsg,
SubmitApprovedTransferMsg,
SubmitApprovedIbcTransferMsg,
SubmitApprovedUnbondMsg,
SubmitApprovedWithdrawMsg,
} from "background/approvals";
Expand All @@ -21,7 +22,11 @@ export enum TopLevelRoute {

export type SupportedTx = Extract<
TxType,
TxType.Bond | TxType.Unbond | TxType.Transfer | TxType.Withdraw
| TxType.Bond
| TxType.Unbond
| TxType.Transfer
| TxType.IBCTransfer
| TxType.Withdraw
>;

export type ApproveMsg = new (msgId: string, password: string) => unknown &
Expand All @@ -31,5 +36,6 @@ export const txMap: Map<SupportedTx, ApproveMsg> = new Map([
[TxType.Bond, SubmitApprovedBondMsg],
[TxType.Unbond, SubmitApprovedUnbondMsg],
[TxType.Transfer, SubmitApprovedTransferMsg],
[TxType.IBCTransfer, SubmitApprovedIbcTransferMsg],
[TxType.Withdraw, SubmitApprovedWithdrawMsg],
]);
30 changes: 30 additions & 0 deletions apps/extension/src/background/approvals/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import {
ApproveBondMsg,
ApproveUnbondMsg,
ApproveTransferMsg,
ApproveIbcTransferMsg,
ApproveWithdrawMsg,
} from "provider";
import {
RejectTxMsg,
SubmitApprovedTransferMsg,
SubmitApprovedIbcTransferMsg,
SubmitApprovedBondMsg,
SubmitApprovedUnbondMsg,
SubmitApprovedWithdrawMsg,
Expand All @@ -22,6 +24,12 @@ export const getHandler: (service: ApprovalsService) => Handler = (service) => {
env,
msg as ApproveTransferMsg
);
case ApproveIbcTransferMsg:
return handleApproveIbcTransferMsg(service)(
env,
msg as ApproveIbcTransferMsg
);

case ApproveBondMsg:
return handleApproveBondMsg(service)(env, msg as ApproveBondMsg);
case ApproveUnbondMsg:
Expand All @@ -38,6 +46,12 @@ export const getHandler: (service: ApprovalsService) => Handler = (service) => {
env,
msg as SubmitApprovedTransferMsg
);
case SubmitApprovedIbcTransferMsg:
return handleSubmitApprovedIBCTransferMsg(service)(
env,
msg as SubmitApprovedIbcTransferMsg
);

case SubmitApprovedBondMsg:
return handleSubmitApprovedBondMsg(service)(
env,
Expand Down Expand Up @@ -67,6 +81,14 @@ const handleApproveTransferMsg: (
};
};

const handleApproveIbcTransferMsg: (
service: ApprovalsService
) => InternalHandler<ApproveIbcTransferMsg> = (service) => {
return async (_, { txMsg, accountType }) => {
return await service.approveIbcTransfer(txMsg, accountType);
};
};

const handleRejectTxMsg: (
service: ApprovalsService
) => InternalHandler<RejectTxMsg> = (service) => {
Expand All @@ -83,6 +105,14 @@ const handleSubmitApprovedTransferMsg: (
};
};

const handleSubmitApprovedIBCTransferMsg: (
service: ApprovalsService
) => InternalHandler<SubmitApprovedIbcTransferMsg> = (service) => {
return async (_, { msgId, password }) => {
return await service.submitIbcTransfer(msgId, password);
};
};

const handleApproveBondMsg: (
service: ApprovalsService
) => InternalHandler<ApproveBondMsg> = (service) => {
Expand Down
4 changes: 4 additions & 0 deletions apps/extension/src/background/approvals/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Router } from "router";
import {
ApproveBondMsg,
ApproveTransferMsg,
ApproveIbcTransferMsg,
ApproveUnbondMsg,
ApproveWithdrawMsg,
} from "provider";
Expand All @@ -10,6 +11,7 @@ import {
SubmitApprovedBondMsg,
SubmitApprovedUnbondMsg,
SubmitApprovedTransferMsg,
SubmitApprovedIbcTransferMsg,
SubmitApprovedWithdrawMsg,
} from "./messages";

Expand All @@ -20,12 +22,14 @@ import { getHandler } from "./handler";
export function init(router: Router, service: ApprovalsService): void {
router.registerMessage(ApproveBondMsg);
router.registerMessage(ApproveTransferMsg);
router.registerMessage(ApproveIbcTransferMsg);
router.registerMessage(ApproveUnbondMsg);
router.registerMessage(ApproveWithdrawMsg);
router.registerMessage(RejectTxMsg);
router.registerMessage(SubmitApprovedBondMsg);
router.registerMessage(SubmitApprovedUnbondMsg);
router.registerMessage(SubmitApprovedTransferMsg);
router.registerMessage(SubmitApprovedIbcTransferMsg);
router.registerMessage(SubmitApprovedWithdrawMsg);

router.addHandler(ROUTE, getHandler(service));
Expand Down
32 changes: 32 additions & 0 deletions apps/extension/src/background/approvals/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ROUTE } from "./constants";
enum MessageType {
RejectTx = "reject-tx",
SubmitApprovedTransfer = "submit-approved-transfer",
SubmitApprovedIbcTransfer = "submit-approved-ibc-transfer",
SubmitApprovedBond = "submit-approved-bond",
SubmitApprovedUnbond = "submit-approved-unbond",
SubmitApprovedWithdraw = "submit-approved-withdraw",
Expand Down Expand Up @@ -65,6 +66,37 @@ export class SubmitApprovedTransferMsg extends Message<void> {
}
}

export class SubmitApprovedIbcTransferMsg extends Message<void> {
public static type(): MessageType {
return MessageType.SubmitApprovedIbcTransfer;
}

constructor(public readonly msgId: string, public readonly password: string) {
super();
}

validate(): void {
if (!this.msgId) {
throw new Error("msgId must not be empty!");
}
if (!this.password) {
throw new Error(
"Password is required to submitTx for this type of account!"
);
}

return;
}

route(): string {
return ROUTE;
}

type(): string {
return SubmitApprovedIbcTransferMsg.type();
}
}

export class SubmitApprovedBondMsg extends Message<void> {
public static type(): MessageType {
return MessageType.SubmitApprovedBond;
Expand Down
73 changes: 60 additions & 13 deletions apps/extension/src/background/approvals/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { deserialize } from "@dao-xyz/borsh";

import {
AccountType,
IbcTransferMsgValue,
SubmitBondMsgValue,
SubmitUnbondMsgValue,
SubmitWithdrawMsgValue,
Expand All @@ -24,7 +25,7 @@ export class ApprovalsService {
protected readonly connectedTabsStore: KVStore<TabStore[]>,
protected readonly keyRingService: KeyRingService,
protected readonly ledgerService: LedgerService
) {}
) { }

// Deserialize transfer details and prompt user
async approveTransfer(txMsg: string, type: AccountType): Promise<void> {
Expand All @@ -42,9 +43,41 @@ export class ApprovalsService {
tx: { publicKey = "" },
} = txDetails;
const amount = new BigNumber(amountBN.toString());
const baseUrl = `${browser.runtime.getURL("approvals.html")}#/approve-tx/${
TxType.Transfer
}`;
const baseUrl = `${browser.runtime.getURL("approvals.html")}#/approve-tx/${TxType.Transfer
}`;

const url = paramsToUrl(baseUrl, {
msgId,
source,
target,
tokenAddress,
amount: amount.toString(),
accountType: type,
publicKey,
});

this._launchApprovalWindow(url);
}

// Deserialize IBC transfer details and prompt user
async approveIbcTransfer(txMsg: string, type: AccountType): Promise<void> {
const txMsgBuffer = Buffer.from(fromBase64(txMsg));
const msgId = uuid();
await this.txStore.set(msgId, txMsg);

// Decode tx details and launch approval screen
const txDetails = deserialize(txMsgBuffer, IbcTransferMsgValue);
const {
source,
receiver: target,
token: tokenAddress,
amount: amountBN,
tx: { publicKey = "" },
} = txDetails;

const amount = new BigNumber(amountBN.toString());
const baseUrl = `${browser.runtime.getURL("approvals.html")}#/approve-tx/${TxType.IBCTransfer
}`;

const url = paramsToUrl(baseUrl, {
msgId,
Expand Down Expand Up @@ -75,9 +108,8 @@ export class ApprovalsService {
tx: { publicKey = "" },
} = txDetails;
const amount = new BigNumber(amountBN.toString());
const baseUrl = `${browser.runtime.getURL("approvals.html")}#/approve-tx/${
TxType.Bond
}`;
const baseUrl = `${browser.runtime.getURL("approvals.html")}#/approve-tx/${TxType.Bond
}`;

const url = paramsToUrl(baseUrl, {
msgId,
Expand Down Expand Up @@ -106,9 +138,8 @@ export class ApprovalsService {
tx: { publicKey = "" },
} = txDetails;
const amount = new BigNumber(amountBN.toString());
const baseUrl = `${browser.runtime.getURL("approvals.html")}#/approve-tx/${
TxType.Unbond
}`;
const baseUrl = `${browser.runtime.getURL("approvals.html")}#/approve-tx/${TxType.Unbond
}`;

const url = paramsToUrl(baseUrl, {
msgId,
Expand All @@ -135,9 +166,8 @@ export class ApprovalsService {
validator,
tx: { publicKey = "" },
} = txDetails;
const baseUrl = `${browser.runtime.getURL("approvals.html")}#/approve-tx/${
TxType.Withdraw
}`;
const baseUrl = `${browser.runtime.getURL("approvals.html")}#/approve-tx/${TxType.Withdraw
}`;

const url = paramsToUrl(baseUrl, {
msgId,
Expand All @@ -149,6 +179,7 @@ export class ApprovalsService {

this._launchApprovalWindow(url);
}

// Remove pending transaction from storage
async rejectTx(msgId: string): Promise<void> {
await this._clearPendingTx(msgId);
Expand All @@ -170,6 +201,22 @@ export class ApprovalsService {
throw new Error("Pending Transfer tx not found!");
}

// Authenticate keyring and submit approved IBC transfer transaction from storage
async submitIbcTransfer(msgId: string, password: string): Promise<void> {
await this.keyRingService.unlock(password);

// Fetch pending transfer tx
const tx = await this.txStore.get(msgId);

if (tx) {
await this.keyRingService.submitIbcTransfer(tx, msgId);

return await this._clearPendingTx(msgId);
}

throw new Error("Pending Transfer tx not found!");
}

// Authenticate keyring and submit approved bond transaction from storage
async submitBond(msgId: string, password: string): Promise<void> {
await this.keyRingService.unlock(password);
Expand Down
Loading

0 comments on commit 665e25f

Please sign in to comment.