Skip to content

Commit

Permalink
Merge pull request #281 from arconnectio/staging
Browse files Browse the repository at this point in the history
ArConnect 1.7.0
  • Loading branch information
nicholaswma authored Apr 2, 2024
2 parents 638f2c5 + cdae74e commit 5cc7bb7
Show file tree
Hide file tree
Showing 19 changed files with 704 additions and 213 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "arconnect",
"displayName": "ArConnect",
"version": "1.6.1",
"version": "1.7.1",
"description": "__MSG_extensionDescription__",
"author": "th8ta",
"packageManager": "yarn@1.22.18",
Expand Down
1 change: 1 addition & 0 deletions src/api/modules/connect/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type AuthType =
| "unlock"
| "token"
| "sign"
| "signMessage"
| "signature";

export interface AuthData {
Expand Down
13 changes: 13 additions & 0 deletions src/api/modules/sign/sign_auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,16 @@ export const signAuth = (
});
}
);

export const signAuthMessage = (dataToSign: Uint8Array) =>
new Promise<AuthResult<{ id: string; signature: string } | undefined>>(
(resolve, reject) => {
// start auth
authenticate({
type: "signMessage",
data: Buffer.from(dataToSign).toString("base64")
})
.then((res) => resolve(res))
.catch((err) => reject(err));
}
);
53 changes: 29 additions & 24 deletions src/api/modules/sign_message/sign_message.background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { getActiveKeyfile } from "~wallets";
import browser from "webextension-polyfill";
import {
isArrayBuffer,
isLocalWallet,
isNotCancelError,
isNumberArray,
isSignMessageOptions
} from "~utils/assertions";
import { signAuthMessage } from "../sign/sign_auth";
import Arweave from "arweave";

const background: ModuleFunction<number[]> = async (
_,
Expand Down Expand Up @@ -39,32 +40,36 @@ const background: ModuleFunction<number[]> = async (

// ensure that the currently selected
// wallet is not a local wallet
isLocalWallet(activeWallet);
if (activeWallet.type === "local") {
// get signing key using the jwk
const cryptoKey = await crypto.subtle.importKey(
"jwk",
activeWallet.keyfile,
{
name: "RSA-PSS",
hash: options.hashAlgorithm
},
false,
["sign"]
);

// get signing key using the jwk
const cryptoKey = await crypto.subtle.importKey(
"jwk",
activeWallet.keyfile,
{
name: "RSA-PSS",
hash: options.hashAlgorithm
},
false,
["sign"]
);
// hashing 2 times ensures that the app is not draining the user's wallet
// credits to Arweave.app
const signature = await crypto.subtle.sign(
{ name: "RSA-RSS", saltLength: 32 },
cryptoKey,
hash
);

// hashing 2 times ensures that the app is not draining the user's wallet
// credits to Arweave.app
const signature = await crypto.subtle.sign(
{ name: "RSA-PSS", saltLength: 32 },
cryptoKey,
hash
);
// remove wallet from memory
freeDecryptedWallet(activeWallet.keyfile);

// remove wallet from memory
freeDecryptedWallet(activeWallet.keyfile);

return Array.from(new Uint8Array(signature));
return Array.from(new Uint8Array(signature));
} else {
const res = await signAuthMessage(dataToSign);
const sig = Arweave.utils.b64UrlToBuffer(res.data.signature);
return Array.from(sig);
}
};

export default background;
84 changes: 84 additions & 0 deletions src/components/auth/Message.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Card, Spacer, Text } from "@arconnect/components";
import browser from "webextension-polyfill";
import { useMemo, useState } from "react";
import styled from "styled-components";

export default function Message({ message }: Props) {
// message decode type
const [decodeType, setDecodeType] = useState("UTF-8");
const availableDecodeTypes = [
"utf-8",
"hex",
"ibm866",
"mac",
"windows-1251",
"gbk",
"utf-16"
];

// current message
const msg = useMemo(() => {
if (typeof message === "undefined") return "";
const messageBytes = new Uint8Array(message);

// handle hex
if (decodeType === "hex") {
return [...new Uint8Array(messageBytes.buffer)]
.map((v) => "0x" + v.toString(16).padStart(2, "0"))
.join(" ");
}

// handle other types
return new TextDecoder(decodeType).decode(messageBytes);
}, [message, decodeType]);

return (
<>
<MessageHeader>
<Text noMargin>{browser.i18n.getMessage("signature_message")}</Text>
<EncodingSelect onChange={(e) => setDecodeType(e.target.value)}>
{availableDecodeTypes.map((type, i) => (
<option value={type} key={i} selected={type === decodeType}>
{type}
</option>
))}
</EncodingSelect>
</MessageHeader>
<Spacer y={0.3} />
<Card smallPadding>
<MessageText>{msg}</MessageText>
</Card>
</>
);
}

interface Props {
message?: number[];
}

const MessageHeader = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
p,
select {
font-size: 0.95rem;
}
`;

const EncodingSelect = styled.select`
font-weight: 500;
color: rgb(${(props) => props.theme.secondaryText});
outline: none;
border: none;
padding: 0;
margin: 0;
background-color: transparent;
`;

const MessageText = styled(Text).attrs({
noMargin: true
})`
font-size: 0.9rem;
`;
52 changes: 33 additions & 19 deletions src/components/dashboard/Tokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import styled from "styled-components";
import PermissionCheckbox from "~components/auth/PermissionCheckbox";
import browser from "webextension-polyfill";
import { Button, Label, Spacer, Text } from "@arconnect/components";
import { useAoTokens } from "~tokens/aoTokens/ao";
import { type TokenInfoWithBalance } from "~tokens/aoTokens/ao";

export default function Tokens() {
// tokens
Expand All @@ -20,14 +20,19 @@ export default function Tokens() {
},
[]
);

const [aoTokens] = useAoTokens();
const [aoTokens] = useStorage<TokenInfoWithBalance[]>(
{
key: "ao_tokens",
instance: ExtensionStorage
},
[]
);

const enhancedAoTokens = useMemo(() => {
return aoTokens.map((token) => ({
id: token.id,
id: token.processId,
defaultLogo: token.Logo,
balance: token.balance,
balance: 0,
ticker: token.Ticker,
type: "asset" as TokenType,
name: token.Name
Expand Down Expand Up @@ -62,28 +67,35 @@ export default function Tokens() {
);

useEffect(() => {
if (activeTokenSetting === "new") {
if (activeTokenSetting === "new" || !matches) {
return;
}
if (!matches) return;

const firstToken = tokens?.[0];

const allTokens = [...tokens, ...enhancedAoTokens];

// return if there is a wallet present in params
if (
!firstToken ||
(!!activeTokenSetting && !!tokens.find((w) => w.id == activeTokenSetting))
activeTokenSetting &&
allTokens.some((t) => t.id === activeTokenSetting)
) {
return;
}

setLocation("/tokens/" + firstToken.id);
}, [tokens, activeTokenSetting]);
if (allTokens.length > 0) {
setLocation("/tokens/" + allTokens[0].id);
}
}, [tokens, enhancedAoTokens, activeTokenSetting, matches]);

const addToken = () => {
setLocation("/tokens/new");
};

const handleTokenClick = (token) => {
setLocation(`/tokens/${token.id}`);
};

return (
<Wrapper>
<div>
Expand Down Expand Up @@ -114,24 +126,26 @@ export default function Tokens() {
/>
))}
<Spacer y={2} />
{enhancedAoTokens.length > 0 && (
{enhancedAoTokens.length > 0 && aoSettingsState && (
<>
<Label style={{ paddingLeft: "4px", margin: "0" }}>
ao tokens
</Label>
{enhancedAoTokens.map((token) => (
<TokenListItem
token={token}
ao={true}
active={activeTokenSetting === token.id}
key={token.id}
/>
<div onClick={() => handleTokenClick(token)} key={token.id}>
<TokenListItem
token={token}
ao={true}
active={activeTokenSetting === token.id}
key={token.id}
/>
</div>
))}
</>
)}
</Reorder.Group>
</div>
<Button fullWidth onClick={addToken} disabled={!aoSettingsState}>
<Button fullWidth onClick={addToken}>
{browser.i18n.getMessage("import_token")}
</Button>
</Wrapper>
Expand Down
45 changes: 23 additions & 22 deletions src/components/dashboard/list/TokenListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { concatGatewayURL } from "~gateways/utils";
import aoLogo from "url:/assets/ecosystem/ao-logo.svg";
import { getUserAvatar } from "~lib/avatar";

export default function TokenListItem({ token, active, ao }: Props) {
export default function TokenListItem({ token, active, ao, onClick }: Props) {
// format address
const formattedAddress = useMemo(
() => formatAddress(token.id, 8),
Expand Down Expand Up @@ -59,44 +59,39 @@ export default function TokenListItem({ token, active, ao }: Props) {
setImage(viewblock.getTokenLogo(token.id));
}
})();
}, [token, theme, gateway]);
}, [token, theme, gateway, ao]);

// router
const [, setLocation] = useLocation();

return ao ? (
<BaseElement
ao={true}
title={`${token.name} (${token.ticker})`}
description={
<div style={{ display: "flex", gap: "8px" }}>
{formattedAddress}
<Image src={aoLogo} alt="ao logo" />
</div>
}
active={active}
>
<TokenLogo src={image} />
</BaseElement>
) : (
const handleClick = () => {
if (onClick) {
onClick();
} else {
setLocation(`/tokens/${token.id}`);
}
};

return (
<Reorder.Item
as="div"
value={token}
id={token.id}
dragListener={false}
dragControls={dragControls}
onClick={() => setLocation(`/tokens/${token.id}`)}
onClick={handleClick}
>
<BaseElement
title={`${token.name} (${token.ticker})`}
description={
<>
<DescriptionWrapper>
{formattedAddress}
<TokenType>{token.type}</TokenType>
</>
{ao && <Image src={aoLogo} alt="ao logo" />}
{!ao && <TokenType>{token.type}</TokenType>}
</DescriptionWrapper>
}
active={active}
dragControls={dragControls}
dragControls={!ao ? dragControls : null}
>
<TokenLogo src={image} />
</BaseElement>
Expand All @@ -111,6 +106,11 @@ const Image = styled.img`
border-radius: 2px;
`;

const DescriptionWrapper = styled.div`
display: flex;
gap: 8px;
`;

const TokenLogo = styled.img.attrs({
alt: "token-logo",
draggable: false
Expand Down Expand Up @@ -140,4 +140,5 @@ interface Props {
token: Token;
ao?: boolean;
active: boolean;
onClick?: () => void;
}
Loading

0 comments on commit 5cc7bb7

Please sign in to comment.