Skip to content

Commit

Permalink
Add swap button (#256)
Browse files Browse the repository at this point in the history
  • Loading branch information
armaniferrante authored Jun 5, 2021
1 parent f15c15a commit 9c9f1d4
Show file tree
Hide file tree
Showing 7 changed files with 449 additions and 204 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
language: node_js
node_js: 10
node_js: 14
dist: bionic
cache: yarn
script: yarn build
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
"private": true,
"dependencies": {
"@ledgerhq/hw-transport-webusb": "^5.45.0",
"@material-ui/core": "^4.11.2",
"@material-ui/core": "^4.11.4",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.58",
"@project-serum/anchor": "^0.7.0",
"@project-serum/serum": "^0.13.33",
"@solana/spl-token-registry": "^0.2.1",
"@solana/web3.js": "^0.87.2",
"@project-serum/swap-ui": "^0.1.0",
"@solana/spl-token-registry": "^0.2.102",
"@solana/web3.js": "^1.10.1",
"@testing-library/jest-dom": "^5.11.6",
"@testing-library/react": "^11.2.2",
"@testing-library/user-event": "^12.6.0",
Expand All @@ -24,10 +27,12 @@
"buffer-layout": "^1.2.0",
"ed25519-hd-key": "^1.2.0",
"immutable-tuple": "^0.4.10",
"material-ui-popup-state": "^1.8.3",
"mdi-material-ui": "^6.21.0",
"notistack": "^1.0.2",
"qrcode.react": "^1.0.0",
"react": "^17.0.1",
"react-async-hook": "^3.6.2",
"react-dom": "^17.0.1",
"react-scripts": "4.0.1",
"tweetnacl": "^1.0.3",
Expand Down
2 changes: 2 additions & 0 deletions src/components/BalancesList.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import ListItemIcon from '@material-ui/core/ListItemIcon';
import TokenIcon from './TokenIcon';
import EditAccountNameDialog from './EditAccountNameDialog';
import MergeAccountsDialog from './MergeAccountsDialog';
import SwapButton from './SwapButton';

const balanceFormat = new Intl.NumberFormat(undefined, {
minimumFractionDigits: 4,
Expand Down Expand Up @@ -227,6 +228,7 @@ export default function BalancesList() {
/>
</IconButton>
</Tooltip>
<SwapButton />
<Tooltip title="Merge Tokens" arrow>
<IconButton
size={iconSize}
Expand Down
99 changes: 99 additions & 0 deletions src/components/SwapButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React from 'react';
import { Tooltip, Popover, IconButton } from '@material-ui/core';
import SwapHoriz from '@material-ui/icons/SwapHoriz';
import PopupState, { bindTrigger, bindPopover } from 'material-ui-popup-state';
import Swap from '@project-serum/swap-ui';
import { Provider } from '@project-serum/anchor';
import { Signer, Connection, TransactionSignature } from '@solana/web3.js';
import { TokenListContainer } from '@solana/spl-token-registry';
import { useTokenInfos } from '../utils/tokens/names';
import { useSendTransaction } from '../utils/notifications';
import { useWallet } from '../utils/wallet';
import { useConnection } from '../utils/connection';

export default function SwapButton() {
const [sendTransaction] = useSendTransaction();
const connection = useConnection();
const wallet = useWallet();
const tokenInfos = useTokenInfos();
const tokenList = tokenInfos && new TokenListContainer(tokenInfos);
const provider = new NotifyingProvider(connection, wallet, sendTransaction);

return (
tokenList && (
<PopupState variant="popover">
{(popupState) => (
<div style={{ display: 'flex' }}>
<Tooltip title="Swap Tokens">
<IconButton {...bindTrigger(popupState)}>
<SwapHoriz />
</IconButton>
</Tooltip>
<Popover
{...bindPopover(popupState)}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
PaperProps={{ style: { borderRadius: '10px' } }}
disableRestoreFocus
keepMounted
>
<Swap provider={provider} tokenList={tokenList} />
</Popover>
</div>
)}
</PopupState>
)
);
}

class NotifyingProvider extends Provider {
constructor(
connection: Connection,
wallet: Wallet,
sendTransaction: (Promise<TransactionSignature>, Function) => void,
) {
super(connection, wallet, {
commitment: 'recent',
});
this.sendTransaction = sendTransaction;
}

async send(
tx: Transaction,
signers?: Array<Signer | undefined>,
opts?: ConfirmOptions,
): Promise<TransactionSignature> {
return new Promise((onSuccess, onError) => {
this.sendTransaction(super.send(tx, signers, opts), {
onSuccess,
onError,
});
});
}

async sendAll(
txs: Array<{ tx: Transaction, signers: Array<Signer | undefined> }>,
opts?: ConfirmOptions,
): Promise<Array<TransactionSignature>> {
return new Promise(async (resolve, onError) => {
let txSigs: Array<TransactionSignature> = [];
for (const tx of txs) {
txSigs.push(
await new Promise((onSuccess) => {
this.sendTransaction(super.send(tx.tx, tx.signers, opts), {
onSuccess,
onError,
});
}),
);
}
resolve(txSigs);
});
}
}
88 changes: 2 additions & 86 deletions src/utils/connection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import {
PublicKey,
} from '@solana/web3.js';
import tuple from 'immutable-tuple';
import { struct } from 'superstruct';
import assert from 'assert';
import * as anchor from '@project-serum/anchor';
import { useLocalStorageState, useRefEqual } from './utils';
import { refreshCache, setCache, useAsyncData } from './fetch-loop';

Expand Down Expand Up @@ -130,88 +129,5 @@ export async function getMultipleSolanaAccounts(
): Promise<
Array<null | { publicKey: PublicKey; account: AccountInfo<Buffer> }>
> {
const args = [publicKeys.map((k) => k.toBase58()), { commitment: 'recent' }];
// @ts-ignore
const unsafeRes = await connection._rpcRequest('getMultipleAccounts', args);
const res = GetMultipleAccountsAndContextRpcResult(unsafeRes);
if (res.error) {
throw new Error(
'failed to get info about accounts ' +
publicKeys.map((k) => k.toBase58()).join(', ') +
': ' +
res.error.message,
);
}
assert(typeof res.result !== 'undefined');
const accounts: Array<null | {
executable: any;
owner: PublicKey;
lamports: any;
data: Buffer;
}> = [];
for (const account of res.result.value) {
let value: {
executable: any;
owner: PublicKey;
lamports: any;
data: Buffer;
} | null = null;
if (res.result.value && account) {
const { executable, owner, lamports, data } = account;
assert(data[1] === 'base64');
value = {
executable,
owner: new PublicKey(owner),
lamports,
data: Buffer.from(data[0], 'base64'),
};
}
accounts.push(value);
}
return accounts.map((account, idx) => {
return account === null
? null
: {
publicKey: publicKeys[idx],
account,
};
});
}

function jsonRpcResult(resultDescription: any) {
const jsonRpcVersion = struct.literal('2.0');
return struct.union([
struct({
jsonrpc: jsonRpcVersion,
id: 'string',
error: 'any',
}),
struct({
jsonrpc: jsonRpcVersion,
id: 'string',
error: 'null?',
result: resultDescription,
}),
]);
}

function jsonRpcResultAndContext(resultDescription: any) {
return jsonRpcResult({
context: struct({
slot: 'number',
}),
value: resultDescription,
});
return anchor.utils.getMultipleAccounts(connection, publicKeys);
}

const AccountInfoResult = struct({
executable: 'boolean',
owner: 'string',
lamports: 'number',
data: 'any',
rentEpoch: 'number?',
});

export const GetMultipleAccountsAndContextRpcResult = jsonRpcResultAndContext(
struct.array([struct.union(['null', AccountInfoResult])]),
);
5 changes: 1 addition & 4 deletions src/utils/walletProvider/localStorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ function deriveSeed(seed, walletIndex, derivationPath, accountIndex) {
export class LocalStorageWalletProvider {
constructor(args) {
this.account = args.account;
this.publicKey = this.account.publicKey;
}

init = async () => {
Expand All @@ -56,10 +57,6 @@ export class LocalStorageWalletProvider {
return this;
};

get publicKey() {
return this.account.publicKey;
}

signTransaction = async (transaction) => {
transaction.partialSign(this.account);
return transaction;
Expand Down
Loading

0 comments on commit 9c9f1d4

Please sign in to comment.