8000 Decode method name from encoded data when sending Tx by kautukkundan · Pull Request #56 · getwax/bls-wallet · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
This repository was archived by the owner on Nov 5, 2023. It is now read-only.

Decode method name from encoded data when sending Tx #56

Merged
merged 5 commits into from
Nov 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contracts/shared/lib/hubble-bls
Submodule hubble-bls added at b37704
2 changes: 2 additions & 0 deletions extension/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ PRIVATE_KEY_STORAGE_KEY=default-private-key
AGGREGATOR_URL=http://localhost:3000
CHAIN_RPC_URL=http://localhost:8545
CREATE_TX_URL=
PROVIDER_URL=
ETHERSCAN_KEY=

VERIFICATION_GATEWAY_ADDRESS=0xCbd1D63d0Ca2228484a7772733717e934CD52aC3
16 changes: 4 additions & 12 deletions extension/source/Background/RequestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import App from '../App';
import addErrorContext from '../common/addErrorContext';
import RpcMap from '../common/RpcMap';
import validateOptionalStringRecord from '../common/validateOptionalStringRecord';
import formatBalance from '../Popup/helpers/formatBalance';
import formatCompactAddress from '../Popup/helpers/formatCompactAddress';
import promptUser from './promptUser';

export default function RequestHandler(
Expand Down Expand Up @@ -59,16 +57,10 @@ export default function RequestHandler(
throw new Error('No wallet available');
}

let promptText: string;
if (tx.data === '0x') {
promptText = `ETH Transfer
${formatBalance(tx.value, 'ETH')}
to ${formatCompactAddress(tx.to)}`;
} else {
promptText = `Contract Interaction ${formatCompactAddress(tx.to)}
value ${formatBalance(tx.value, 'ETH')}
data ${tx.data}`;
}
const promptText = `
&to=${tx.to}
&data=${tx.data}
&value=${tx.value}`;

const promptResult = await promptUser({
promptText,
Expand Down
7 changes: 1 addition & 6 deletions extension/source/Background/promptUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import getPropOrUndefined from '../helpers/getPropOrUndefined';

export default function promptUser(opt: {
promptText: string;
buttons?: string[];
}): Promise<string | undefined> {
const cleanupTasks = new TaskQueue();

Expand All @@ -23,11 +22,7 @@ export default function promptUser(opt: {
}

const popup = await browser.windows.create({
url: browser.runtime.getURL(
`confirm.html?promptText=${opt.promptText}&id=${id}&buttons=${(
opt.buttons ?? ['Yes', 'No']
).join(',')}`,
),
url: browser.runtime.getURL(`confirm.html?${opt.promptText}&id=${id}`),
type: 'popup',
width: popupWidth,
height: 500,
Expand Down
104 changes: 50 additions & 54 deletions extension/source/Confirm/Confirm.tsx
Original file line number Diff line number Diff line change
@@ -1,69 +1,65 @@
import * as React from 'react';
import { ethers } from 'ethers';
import React, { useEffect, useState } from 'react';
import { browser } from 'webextension-polyfill-ts';
import TaskQueue from '../common/TaskQueue';

// components, styles and UI
import Button from '../components/Button';
import CompactQuillHeading from '../components/CompactQuillHeading';
import { useInputDecode } from '../hooks/useInputDecode';
import formatCompactAddress from '../Popup/helpers/formatCompactAddress';

type Props = {
_?: undefined;
};
const Confirm: React.FunctionComponent = () => {
const [id, setId] = useState<string>();
const [to, setTo] = useState<string>('0x');
const [value, setValue] = useState<string>('0');
const [data, setData] = useState<string>('0x');

type State = {
_?: undefined;
};
const { loading, method } = useInputDecode(data, to);

export default class Popup extends React.Component<Props, State> {
cleanupTasks = new TaskQueue();
const cleanupTasks = new TaskQueue();

constructor(props: Props) {
super(props);
useEffect(() => {
const params = new URL(window.location.href).searchParams;
setId(params.get('id') || '0');
setTo(params.get('to') || '0x');
setValue(params.get('value') || '0');
setData(params.get('data') || '0x');

this.state = {};
}
return cleanupTasks.run();
}, []);

componentWillUnmount(): void {
this.cleanupTasks.run();
}
const respondTx = (result: string) => {
browser.runtime.sendMessage(undefined, { id, result });
};

render(): React.ReactNode {
const params = new URL(window.location.href).searchParams;
const id = params.get('id');
const promptText = params.get('promptText') ?? '(promptText not set)';
const buttons = parseButtons(params.get('buttons'));
return (
<div className="confirm">
<div className="section">
<CompactQuillHeading />
</div>
<div className="section prompt">
{loading ? (
'loading...'
) : (
<>
<div>{method}</div>
<div>to: {formatCompactAddress(to)}</div>
<div>value: {ethers.utils.formatEther(value)} ETH</div>
<div>
data:
<div className="data">{data}</div>
</div>

return (
<div className="confirm">
<div className="section">
<CompactQuillHeading />
</div>
<div className="section prompt">
<div>{promptText}</div>
<div />
{buttons.map((btnText, i) => (
<Button
highlight={i === 0}
=> {
browser.runtime.sendMessage(undefined, { id, result: btnText });
}}
key={btnText}
>
{btnText}
<Button highlight => respondTx('Yes')}>
Confirm
</Button>
))}
</div>
<Button => respondTx('No')}>Reject</Button>
</>
)}
</div>
);
}
}

function parseButtons(buttonsStr: string | null): string[] {
if (buttonsStr === null) {
return ['(buttons not set)'];
}

if (buttonsStr === '') {
return [];
}
</div>
);
};

return buttonsStr.split(',');
}
export default Confirm;
7 changes: 6 additions & 1 deletion extension/source/Confirm/styles.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import "../styles/quill";
@import '../styles/quill';

.quill {
.prompt {
Expand All @@ -7,4 +7,9 @@
flex-direction: column;
gap: 12px;
}

.data {
word-wrap: break-word;
opacity: 0.5;
}
}
2 changes: 1 addition & 1 deletion extension/source/Popup/helpers/formatCompactAddress.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export default function formatCompactAddress(address: string): string {
return `0x ${address.slice(2, 6)} ... ${address.slice(-4)}`;
return `0x${address.slice(2, 6)}...${address.slice(-4)}`;
}
2 changes: 2 additions & 0 deletions extension/source/env.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { requireEnv, requireIntEnv } from './helpers/envTools';

export const CHAIN_ID = requireIntEnv(process.env.CHAIN_ID);
export const PROVIDER_URL = requireEnv(process.env.PROVIDER_URL);
// export const ETHERSCAN_KEY = requireEnv(process.env.ETHERSCAN_KEY);

export const PRIVATE_KEY_STORAGE_KEY = requireEnv(
process.env.PRIVATE_KEY_STORAGE_KEY,
Expand Down
82 changes: 82 additions & 0 deletions extension/source/hooks/useInputDecode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { ethers } from 'ethers';
import { useEffect, useState } from 'react';
import { PROVIDER_URL } from '../env';
import axios from 'axios';

const getParitySigRegistry = () => {
const provider = new ethers.providers.JsonRpcProvider(PROVIDER_URL);
const address = '0x44691B39d1a75dC4E0A0346CBB15E310e6ED1E86';
const abi = [
{
constant: true,
inputs: [{ name: '', type: 'bytes4' }],
name: 'entries',
outputs: [{ name: '', type: 'string' }],
payable: false,
type: 'function',
},
];

return new ethers.Contract(address, abi, provider);
};

const getMethodFromOnChainRegistry = async (data: string) => {
if (data === '0x') return 'SENDING ETH';

const methodID = ethers.utils.hexDataSlice(data, 0, 4);
const registry = getParitySigRegistry();

return registry.entries(methodID);
};

const getMethodFromEtherscan = async (to: string, data: string) => {
const res = await axios.get(
`https://api.etherscan.io/api?module=contract&action=getabi&address=${to}`,
);

if (res.data.result !== 'Contract source code not verified') {
const iface = new ethers.utils.Interface(res.data.result);
return iface.parseTransaction({ data, value: 1 }).name;
}

throw 'Unverified Contract';
};

const formatMethod = (method: string) => {
return method
.split('(')[0]
.replace(/([a-z](?=[A-Z]))/g, '$1 ')
.toUpperCase();
};

export const useInputDecode = (functionData: string, to: string) => {
const [loading, setLoading] = useState<boolean>(true);
const [method, setMethod] = useState<string>('CONTRACT INTERACTION');

useEffect(() => {
const getMethod = async () => {
setLoading(true);

const data = functionData?.replace(/\s+/g, '');

try {
const registryPromise = getMethodFromOnChainRegistry(data);
const etherScanPromise = getMethodFromEtherscan(to, data);
const method = (await registryPromise) ?? (await etherScanPromise);
if (method) {
setMethod(formatMethod(method));
}
} catch (error) {
console.log({ error });
}

setLoading(false);
};

if (functionData) {
getMethod();
}
}, [functionData]);

return { loading, method };
};
0