feat: adds Elrond support (#67)

This commit is contained in:
Radu Mojic
2022-10-31 11:21:33 +01:00
committed by GitHub
parent 8d2240f8d5
commit 907d61cb50
34 changed files with 4807 additions and 3818 deletions
+8
View File
@@ -3,6 +3,14 @@ const nextConfig = {
reactStrictMode: true,
swcMinify: true,
distDir: "build",
webpack(config) {
config.resolve.fallback = {
...config.resolve.fallback,
fs: false,
};
return config;
},
};
module.exports = nextConfig;
+2
View File
@@ -10,6 +10,8 @@
"prettier:write": "prettier --write '**/*.{js,ts,jsx,tsx}'"
},
"dependencies": {
"@elrondnetwork/erdjs": "11.1.1",
"@elrondnetwork/erdjs-walletcore": "2.1.0",
"@ethereumjs/tx": "^3.5.0",
"@polkadot/util-crypto": "^10.1.2",
"@solana/web3.js": "^1.36.0",
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

+55
View File
@@ -0,0 +1,55 @@
import { ChainsMap } from "caip-api";
import { NamespaceMetadata, ChainMetadata } from "../helpers";
// TODO: add `elrond` namespace to `caip-api` package to avoid manual specification here.
export const ElrondChainData: ChainsMap = {
"1": {
id: "elrond:1",
name: "Elrond Mainnet",
rpc: ["https://api.elrond.com"],
slip44: 508,
testnet: false,
},
D: {
id: "elrond:D",
name: "Elrond Devnet",
rpc: ["https://devnet-api.elrond.com"],
slip44: 508,
testnet: true,
},
// Keep only one Test Chain visible
// T: {
// id: "elrond:T",
// name: "Elrond Testnet",
// rpc: ["https://testnet-api.elrond.com"],
// slip44: 508,
// testnet: true,
// },
};
export const ElrondMetadata: NamespaceMetadata = {
// Elrond Mainnet
"1": {
logo: "/assets/elrond_logo.png",
rgb: "0, 0, 0",
},
// Elrond Testnet
T: {
logo: "/assets/elrond_logo.png",
rgb: "0, 0, 0",
},
// Elrond Devnet
D: {
logo: "/assets/elrond_logo.png",
rgb: "0, 0, 0",
},
};
export function getChainMetadata(chainId: string): ChainMetadata {
const reference = chainId.split(":")[1];
const metadata = ElrondMetadata[reference];
if (typeof metadata === "undefined") {
throw new Error(`No chain metadata found for chainId: ${chainId}`);
}
return metadata;
}
+3
View File
@@ -5,6 +5,7 @@ import * as cosmos from "./cosmos";
import * as polkadot from "./polkadot";
import * as solana from "./solana";
import * as near from "./near";
import * as elrond from "./elrond";
import { ChainMetadata, ChainRequestRender } from "../helpers";
@@ -21,6 +22,8 @@ export function getChainMetadata(chainId: string): ChainMetadata {
return solana.getChainMetadata(chainId);
case "near":
return near.getChainMetadata(chainId);
case "elrond":
return elrond.getChainMetadata(chainId);
default:
throw new Error(`No metadata handler for namespace ${namespace}`);
}
@@ -9,6 +9,7 @@ export const DEFAULT_MAIN_CHAINS = [
"cosmos:cosmoshub-4",
"solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ",
"polkadot:91b171bb158e2d3848fa23a9f1c25182",
"elrond:1",
];
export const DEFAULT_TEST_CHAINS = [
@@ -21,6 +22,7 @@ export const DEFAULT_TEST_CHAINS = [
"solana:8E9rvCKLFQia2Y35HXjjpWzj8weVo44K",
"polkadot:e143f23803ac50e8f6f8e62695d1ce9e",
"near:testnet",
"elrond:D",
];
export const DEFAULT_CHAINS = [...DEFAULT_MAIN_CHAINS, ...DEFAULT_TEST_CHAINS];
@@ -96,6 +98,18 @@ export enum DEFAULT_NEAR_METHODS {
export enum DEFAULT_NEAR_EVENTS {}
/**
* ELROND
*/
export enum DEFAULT_ELROND_METHODS {
ELROND_SIGN_TRANSACTION = "erd_signTransaction",
ELROND_SIGN_TRANSACTIONS = "erd_signTransactions",
ELROND_SIGN_MESSAGE = "erd_signMessage",
ELROND_SIGN_LOGIN_TOKEN = "erd_signLoginToken",
}
export enum DEFAULT_ELROND_EVENTS {}
export const DEFAULT_GITHUB_REPO_URL =
"https://github.com/WalletConnect/web-examples/tree/main/dapps/react-dapp-v2";
@@ -8,6 +8,7 @@ import {
} from "react";
import { SolanaChainData } from "../chains/solana";
import { PolkadotChainData } from "../chains/polkadot";
import { ElrondChainData } from "../chains/elrond";
import { ChainNamespaces, getAllChainNamespaces } from "../helpers";
import { NearChainData } from "../chains/near";
@@ -47,6 +48,8 @@ export function ChainDataContextProvider({
chains = PolkadotChainData;
} else if (namespace === "near") {
chains = NearChainData;
} else if (namespace === "elrond") {
chains = ElrondChainData;
} else {
chains = await apiGetChainNamespace(namespace);
}
@@ -31,10 +31,23 @@ import {
DEFAULT_SOLANA_METHODS,
DEFAULT_POLKADOT_METHODS,
DEFAULT_NEAR_METHODS,
DEFAULT_ELROND_METHODS,
} from "../constants";
import { useChainData } from "./ChainDataContext";
import { signatureVerify, cryptoWaitReady } from "@polkadot/util-crypto";
import {
Transaction as ElrondTransaction,
TransactionPayload,
Address,
SignableMessage,
ISignature,
} from "@elrondnetwork/erdjs";
import { UserVerifier } from "@elrondnetwork/erdjs-walletcore/out/userVerifier";
import { Signature } from "@elrondnetwork/erdjs-walletcore/out/signature";
import { IVerifiable } from "@elrondnetwork/erdjs-walletcore/out/interface";
/**
* Types
*/
@@ -72,6 +85,11 @@ interface IContext {
testSignAndSendTransaction: TRpcRequestCallback;
testSignAndSendTransactions: TRpcRequestCallback;
};
elrondRpc: {
testSignMessage: TRpcRequestCallback;
testSignTransaction: TRpcRequestCallback;
testSignTransactions: TRpcRequestCallback;
};
rpcResult?: IFormattedRpcResponse | null;
isRpcRequestPending: boolean;
isTestnet: boolean;
@@ -814,6 +832,198 @@ export function JsonRpcContextProvider({
),
};
// -------- ELROND RPC METHODS --------
const elrondRpc = {
testSignTransaction: _createJsonRpcRequestHandler(
async (
chainId: string,
address: string
): Promise<IFormattedRpcResponse> => {
const reference = chainId.split(":")[1];
const userAddress = new Address(address);
const verifier = UserVerifier.fromAddress(userAddress);
const transactionPayload = new TransactionPayload("testdata");
const testTransaction = new ElrondTransaction({
nonce: 1,
value: "10000000000000000000",
receiver: Address.fromBech32(address),
sender: userAddress,
gasPrice: 1000000000,
gasLimit: 50000,
chainID: reference,
data: transactionPayload,
});
const transaction = testTransaction.toPlainObject();
try {
const result = await client!.request<{ signature: Buffer }>({
chainId,
topic: session!.topic,
request: {
method: DEFAULT_ELROND_METHODS.ELROND_SIGN_TRANSACTION,
params: {
transaction,
},
},
});
testTransaction.applySignature(
new Signature(result.signature),
userAddress
);
const valid = verifier.verify(testTransaction as IVerifiable);
return {
method: DEFAULT_ELROND_METHODS.ELROND_SIGN_TRANSACTION,
address,
valid,
result: result.signature.toString(),
};
} catch (error: any) {
throw new Error(error);
}
}
),
testSignTransactions: _createJsonRpcRequestHandler(
async (
chainId: string,
address: string
): Promise<IFormattedRpcResponse> => {
const reference = chainId.split(":")[1];
const userAddress = new Address(address);
const verifier = UserVerifier.fromAddress(userAddress);
const testTransactionPayload = new TransactionPayload("testdata");
const testTransaction = new ElrondTransaction({
nonce: 1,
value: "10000000000000000000",
receiver: Address.fromBech32(address),
sender: userAddress,
gasPrice: 1000000000,
gasLimit: 50000,
chainID: reference,
data: testTransactionPayload,
});
// no data for this Transaction
const testTransaction2 = new ElrondTransaction({
nonce: 2,
value: "20000000000000000000",
receiver: Address.fromBech32(address),
sender: userAddress,
gasPrice: 1000000000,
gasLimit: 50000,
chainID: reference,
});
const testTransaction3Payload = new TransactionPayload("third");
const testTransaction3 = new ElrondTransaction({
nonce: 3,
value: "300000000000000000",
receiver: Address.fromBech32(address),
sender: userAddress,
gasPrice: 1000000000,
gasLimit: 50000,
chainID: reference,
data: testTransaction3Payload,
});
const transactions = [
testTransaction,
testTransaction2,
testTransaction3,
].map((transaction) => transaction.toPlainObject());
try {
const result = await client!.request<{
signatures: { signature: Buffer }[];
}>({
chainId,
topic: session!.topic,
request: {
method: DEFAULT_ELROND_METHODS.ELROND_SIGN_TRANSACTIONS,
params: {
transactions,
},
},
});
const valid = [
testTransaction,
testTransaction2,
testTransaction3,
].reduce((acc, current, index) => {
current.applySignature(
new Signature(result.signatures[index].signature),
userAddress
);
return acc && verifier.verify(current as IVerifiable);
}, true);
const resultSignatures = result.signatures.map(
(signature: any) => signature.signature
);
return {
method: DEFAULT_ELROND_METHODS.ELROND_SIGN_TRANSACTIONS,
address,
valid,
result: resultSignatures.join(", "),
};
} catch (error: any) {
throw new Error(error);
}
}
),
testSignMessage: _createJsonRpcRequestHandler(
async (
chainId: string,
address: string
): Promise<IFormattedRpcResponse> => {
const userAddress = new Address(address);
const verifier = UserVerifier.fromAddress(userAddress);
const testMessage = new SignableMessage({
address: userAddress,
message: Buffer.from(`Sign this message - ${Date.now()}`, "ascii"),
});
try {
const result = await client!.request<{ signature: Buffer }>({
chainId,
topic: session!.topic,
request: {
method: DEFAULT_ELROND_METHODS.ELROND_SIGN_MESSAGE,
params: {
address,
message: testMessage.message.toString(),
},
},
});
testMessage.applySignature(new Signature(result.signature));
const valid = verifier.verify(testMessage);
return {
method: DEFAULT_ELROND_METHODS.ELROND_SIGN_MESSAGE,
address,
valid,
result: result.signature.toString(),
};
} catch (error: any) {
throw new Error(error);
}
}
),
};
return (
<JsonRpcContext.Provider
value={{
@@ -823,6 +1033,7 @@ export function JsonRpcContextProvider({
solanaRpc,
polkadotRpc,
nearRpc,
elrondRpc,
rpcResult: result,
isRpcRequestPending: pending,
isTestnet,
+4
View File
@@ -105,6 +105,10 @@ export async function apiGetAccountBalance(
address: string,
chainId: string
): Promise<AssetData> {
const namespace = chainId.split(":")[0];
if (namespace !== 'eip155') {
return { balance: "", symbol: "", name: "" };
}
const ethChainId = chainId.split(":")[1];
const rpc = rpcProvidersByChainId[Number(ethChainId)];
if (!rpc) {
@@ -10,6 +10,8 @@ import {
DEFAULT_POLKADOT_METHODS,
DEFAULT_NEAR_METHODS,
DEFAULT_NEAR_EVENTS,
DEFAULT_ELROND_EVENTS,
DEFAULT_ELROND_METHODS,
} from "../constants";
export const getNamespacesFromChains = (chains: string[]) => {
@@ -36,6 +38,8 @@ export const getSupportedMethodsByNamespace = (namespace: string) => {
return Object.values(DEFAULT_POLKADOT_METHODS);
case "near":
return Object.values(DEFAULT_NEAR_METHODS);
case "elrond":
return Object.values(DEFAULT_ELROND_METHODS);
default:
throw new Error(`No default methods for namespace: ${namespace}`);
}
@@ -53,6 +57,8 @@ export const getSupportedEventsByNamespace = (namespace: string) => {
return Object.values(DEFAULT_POLKADOT_EVENTS);
case "near":
return Object.values(DEFAULT_NEAR_EVENTS);
case "elrond":
return Object.values(DEFAULT_ELROND_EVENTS);
default:
throw new Error(`No default events for namespace: ${namespace}`);
}
+33
View File
@@ -13,6 +13,7 @@ import {
DEFAULT_MAIN_CHAINS,
DEFAULT_SOLANA_METHODS,
DEFAULT_POLKADOT_METHODS,
DEFAULT_ELROND_METHODS,
DEFAULT_TEST_CHAINS,
DEFAULT_NEAR_METHODS,
} from "../constants";
@@ -71,6 +72,7 @@ const Home: NextPage = () => {
solanaRpc,
polkadotRpc,
nearRpc,
elrondRpc,
isRpcRequestPending,
rpcResult,
isTestnet,
@@ -240,6 +242,35 @@ const Home: NextPage = () => {
];
};
const getElrondActions = (): AccountAction[] => {
const onSignTransaction = async (chainId: string, address: string) => {
openRequestModal();
await elrondRpc.testSignTransaction(chainId, address);
};
const onSignTransactions = async (chainId: string, address: string) => {
openRequestModal();
await elrondRpc.testSignTransactions(chainId, address);
};
const onSignMessage = async (chainId: string, address: string) => {
openRequestModal();
await elrondRpc.testSignMessage(chainId, address);
};
return [
{
method: DEFAULT_ELROND_METHODS.ELROND_SIGN_TRANSACTION,
callback: onSignTransaction,
},
{
method: DEFAULT_ELROND_METHODS.ELROND_SIGN_TRANSACTIONS,
callback: onSignTransactions,
},
{
method: DEFAULT_ELROND_METHODS.ELROND_SIGN_MESSAGE,
callback: onSignMessage,
},
];
};
const getBlockchainActions = (chainId: string) => {
const [namespace] = chainId.split(":");
switch (namespace) {
@@ -253,6 +284,8 @@ const Home: NextPage = () => {
return getPolkadotActions();
case "near":
return getNearActions();
case "elrond":
return getElrondActions();
default:
break;
}
+2856 -2642
View File
File diff suppressed because it is too large Load Diff