Sign many transactions at once (#60)
This commit is contained in:
parent
d1e6ad7e18
commit
1f6e11107d
|
@ -78,7 +78,10 @@ export default function PopupPage({ opener }) {
|
|||
useEffect(() => {
|
||||
function messageHandler(e) {
|
||||
if (e.origin === origin && e.source === window.opener) {
|
||||
if (e.data.method !== 'signTransaction') {
|
||||
if (
|
||||
e.data.method !== 'signTransaction' &&
|
||||
e.data.method !== 'signAllTransactions'
|
||||
) {
|
||||
postMessage({ error: 'Unsupported method', id: e.data.id });
|
||||
}
|
||||
|
||||
|
@ -106,11 +109,29 @@ export default function PopupPage({ opener }) {
|
|||
|
||||
if (requests.length > 0) {
|
||||
const request = requests[0];
|
||||
assert(request.method === 'signTransaction');
|
||||
const message = bs58.decode(request.params.message);
|
||||
assert(
|
||||
request.method === 'signTransaction' ||
|
||||
request.method === 'signAllTransactions',
|
||||
);
|
||||
|
||||
async function sendSignature() {
|
||||
let messages =
|
||||
request.method === 'signTransaction'
|
||||
? [bs58.decode(request.params.message)]
|
||||
: request.params.messages.map((m) => bs58.decode(m));
|
||||
|
||||
async function onApprove() {
|
||||
setRequests((requests) => requests.slice(1));
|
||||
if (request.method === 'signTransaction') {
|
||||
sendSignature(messages[0]);
|
||||
} else {
|
||||
sendAllSignatures(messages);
|
||||
}
|
||||
if (requests.length === 1) {
|
||||
focusParent();
|
||||
}
|
||||
}
|
||||
|
||||
async function sendSignature(message) {
|
||||
postMessage({
|
||||
result: {
|
||||
signature: await wallet.createSignature(message),
|
||||
|
@ -118,9 +139,19 @@ export default function PopupPage({ opener }) {
|
|||
},
|
||||
id: request.id,
|
||||
});
|
||||
if (requests.length === 1) {
|
||||
focusParent();
|
||||
}
|
||||
|
||||
async function sendAllSignatures(messages) {
|
||||
const signatures = await Promise.all(
|
||||
messages.map((m) => wallet.createSignature(m)),
|
||||
);
|
||||
postMessage({
|
||||
result: {
|
||||
signatures,
|
||||
publicKey: wallet.publicKey.toBase58(),
|
||||
},
|
||||
id: request.id,
|
||||
});
|
||||
}
|
||||
|
||||
function sendReject() {
|
||||
|
@ -138,8 +169,8 @@ export default function PopupPage({ opener }) {
|
|||
key={request.id}
|
||||
autoApprove={autoApprove}
|
||||
origin={origin}
|
||||
message={message}
|
||||
onApprove={sendSignature}
|
||||
messages={messages}
|
||||
onApprove={onApprove}
|
||||
onReject={sendReject}
|
||||
/>
|
||||
);
|
||||
|
@ -269,7 +300,7 @@ function ApproveConnectionForm({ origin, onApprove }) {
|
|||
);
|
||||
}
|
||||
|
||||
function isSafeInstruction(publicKeys, owner, instructions) {
|
||||
function isSafeInstruction(publicKeys, owner, txInstructions) {
|
||||
let unsafe = false;
|
||||
const states = {
|
||||
CREATED: 0,
|
||||
|
@ -292,6 +323,7 @@ function isSafeInstruction(publicKeys, owner, instructions) {
|
|||
return accountStates[pubkey.toBase58()] === states.OWNED;
|
||||
}
|
||||
|
||||
txInstructions.forEach((instructions) => {
|
||||
instructions.forEach((instruction) => {
|
||||
if (!instruction) {
|
||||
unsafe = true;
|
||||
|
@ -347,6 +379,7 @@ function isSafeInstruction(publicKeys, owner, instructions) {
|
|||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Check that all accounts are owned
|
||||
if (
|
||||
|
@ -363,7 +396,7 @@ function isSafeInstruction(publicKeys, owner, instructions) {
|
|||
|
||||
function ApproveSignatureForm({
|
||||
origin,
|
||||
message,
|
||||
messages,
|
||||
onApprove,
|
||||
onReject,
|
||||
autoApprove,
|
||||
|
@ -375,24 +408,30 @@ function ApproveSignatureForm({
|
|||
const [publicKeys] = useWalletPublicKeys();
|
||||
|
||||
const [parsing, setParsing] = useState(true);
|
||||
const [instructions, setInstructions] = useState(null);
|
||||
// An array of arrays, where each element is the set of instructions for a
|
||||
// single transaction.
|
||||
const [txInstructions, setTxInstructions] = useState(null);
|
||||
const buttonRef = useRef();
|
||||
|
||||
const isMultiTx = messages.length > 1;
|
||||
|
||||
useEffect(() => {
|
||||
decodeMessage(connection, wallet, message).then((instructions) => {
|
||||
setInstructions(instructions);
|
||||
Promise.all(messages.map((m) => decodeMessage(connection, wallet, m))).then(
|
||||
(txInstructions) => {
|
||||
setTxInstructions(txInstructions);
|
||||
setParsing(false);
|
||||
});
|
||||
}, [message, connection, wallet]);
|
||||
},
|
||||
);
|
||||
}, [messages, connection, wallet]);
|
||||
|
||||
const validator = useMemo(() => {
|
||||
return {
|
||||
safe:
|
||||
publicKeys &&
|
||||
instructions &&
|
||||
isSafeInstruction(publicKeys, wallet.publicKey, instructions),
|
||||
txInstructions &&
|
||||
isSafeInstruction(publicKeys, wallet.publicKey, txInstructions),
|
||||
};
|
||||
}, [publicKeys, instructions, wallet]);
|
||||
}, [publicKeys, txInstructions, wallet]);
|
||||
|
||||
useEffect(() => {
|
||||
if (validator.safe && autoApprove) {
|
||||
|
@ -460,6 +499,37 @@ function ApproveSignatureForm({
|
|||
}
|
||||
};
|
||||
|
||||
const txLabel = (idx) => {
|
||||
return (
|
||||
<>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Transaction {idx.toString()}
|
||||
</Typography>
|
||||
<Divider style={{ marginTop: 20 }} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const txListItem = (instructions, txIdx) => {
|
||||
const ixs = instructions.map((instruction, i) => (
|
||||
<Box style={{ marginTop: 20 }} key={i}>
|
||||
{getContent(instruction)}
|
||||
<Divider style={{ marginTop: 20 }} />
|
||||
</Box>
|
||||
));
|
||||
|
||||
if (!isMultiTx) {
|
||||
return ixs;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box style={{ marginTop: 20 }} key={txIdx}>
|
||||
{txLabel(txIdx)}
|
||||
{ixs}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardContent>
|
||||
|
@ -478,27 +548,26 @@ function ApproveSignatureForm({
|
|||
style={{ fontWeight: 'bold' }}
|
||||
gutterBottom
|
||||
>
|
||||
Parsing transaction:
|
||||
Parsing transaction{isMultiTx > 0 ? 's' : ''}:
|
||||
</Typography>
|
||||
</div>
|
||||
<Typography style={{ wordBreak: 'break-all' }}>
|
||||
{messages.map((message, idx) => (
|
||||
<Typography key={idx} style={{ wordBreak: 'break-all' }}>
|
||||
{bs58.encode(message)}
|
||||
</Typography>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{instructions
|
||||
{txInstructions
|
||||
? `${origin} wants to:`
|
||||
: `Unknown transaction data`}
|
||||
</Typography>
|
||||
{instructions ? (
|
||||
instructions.map((instruction, i) => (
|
||||
<Box style={{ marginTop: 20 }} key={i}>
|
||||
{getContent(instruction)}
|
||||
<Divider style={{ marginTop: 20 }} />
|
||||
</Box>
|
||||
))
|
||||
{txInstructions ? (
|
||||
txInstructions.map((instructions, txIdx) =>
|
||||
txListItem(instructions, txIdx),
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<Typography
|
||||
|
@ -506,11 +575,13 @@ function ApproveSignatureForm({
|
|||
style={{ fontWeight: 'bold' }}
|
||||
gutterBottom
|
||||
>
|
||||
Unknown transaction:
|
||||
Unknown transaction{isMultiTx > 0 ? 's' : ''}:
|
||||
</Typography>
|
||||
{messages.map((message) => (
|
||||
<Typography style={{ wordBreak: 'break-all' }}>
|
||||
{bs58.encode(message)}
|
||||
</Typography>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{!validator.safe && (
|
||||
|
@ -543,7 +614,7 @@ function ApproveSignatureForm({
|
|||
color="primary"
|
||||
onClick={onApprove}
|
||||
>
|
||||
Approve
|
||||
Approve{isMultiTx ? ' All' : ''}
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
|
|
Loading…
Reference in New Issue