# MPC - Get started with Web3Auth's MPC
## Installation
```bash!
yarn add @toruslabs/tss-client @toruslabs/tss-lib @web3auth-mpc/openlogin-adapter @web3auth-mpc/web3auth
```
## Import the following packages
```jsx!
import { Client } from "@toruslabs/tss-client";
import * as tss from "@toruslabs/tss-lib";
import { OpenloginAdapter } from "@web3auth-mpc/openlogin-adapter";
import { Web3Auth } from "@web3auth-mpc/web3auth";
```
Some other helper libraries too:
```jsx!
import { keccak256, safeatob } from "@toruslabs/openlogin-utils";
import { post } from "@toruslabs/http-helpers";
import BN from "bn.js";
import { io, Socket } from "socket.io-client";
```
> Review: later, why we need this.
```jsx!
import { ec as EC } from "elliptic";
const ec = new EC("secp256k1");
```
### TSS Endpoint and ImportURL
```jsx!
const tssServerEndpoint = "https://swaraj-test-coordinator-1.k8.authnetwork.dev/tss";
const tssImportURL = "https://cloudflare-ipfs.com/ipfs/QmWxSMacBkunyAcKkjuDTU9yCady62n3VGW2gcUEcHg6Vh";
```
## TSS Functions
### `getPublicKeyFromTSSShare`
```jsx
async function getPublicKeyFromTSSShare(tssShare: string, signatures: string[]): Promise<string> {
// check if TSS is available
if (!tssShare || !Array.isArray(signatures) || signatures.length === 0) {
throw new Error("tssShare or signatures not available");
}
const parsedTSSShare = {
share: tssShare.split("-")[0].split(":")[1],
index: tssShare.split("-")[1].split(":")[1],
};
const parsedSignatures = signatures.map((s) => JSON.parse(s));
const chosenSignature = parsedSignatures[Math.floor(Math.random() * parsedSignatures.length)];
const { verifier_name: verifierName, verifier_id: verifierId } = JSON.parse(safeatob(chosenSignature.data));
if (!verifierName || !verifierId) {
throw new Error("verifier_name and verifier_id must be specified");
}
const { share_pub_x: sharePubX, share_pub_y: sharePubY } = await post<{
// eslint-disable-next-line camelcase
share_pub_x: string;
// eslint-disable-next-line camelcase
share_pub_y: string;
}>(`${tssServerEndpoint}/getOrCreateTSSPub`, {
verifier_name: verifierName,
verifier_id: verifierId,
});
const getLagrangeCoeff = (partyIndexes: BN[], partyIndex: BN): BN => {
let upper = new BN(1);
let lower = new BN(1);
for (let i = 0; i < partyIndexes.length; i += 1) {
const otherPartyIndex = partyIndexes[i];
if (!partyIndex.eq(otherPartyIndex)) {
upper = upper.mul(otherPartyIndex.neg());
upper = upper.umod(ec.curve.n);
let temp = partyIndex.sub(otherPartyIndex);
temp = temp.umod(ec.curve.n);
lower = lower.mul(temp).umod(ec.curve.n);
}
}
const delta = upper.mul(lower.invm(ec.curve.n)).umod(ec.curve.n);
return delta;
};
// TODO: extend
const localIndex = 1;
const remoteIndex = 0;
const parties = [0, 1];
const pubKeyPoint = ec
.keyFromPublic({ x: sharePubX, y: sharePubY })
.getPublic()
.mul(
getLagrangeCoeff(
parties.map((p) => new BN(p + 1)),
new BN(remoteIndex + 1)
)
)
.add(
ec
.keyFromPrivate(Buffer.from(parsedTSSShare.share.padStart(64, "0"), "hex"))
.getPublic()
.mul(
getLagrangeCoeff(
parties.map((p) => new BN(p + 1)),
new BN(localIndex + 1)
)
)
);
const pubKeyX = pubKeyPoint.getX().toString(16, 64);
const pubKeyY = pubKeyPoint.getY().toString(16, 64);
const pubKeyHex = `${pubKeyX}${pubKeyY}`;
const pubKey = Buffer.from(pubKeyHex, "hex").toString("base64");
return pubKey;
}
```
### `createSockets`
```jsx!
async function createSockets(wsEndpoints: (string | null | undefined)[]): Promise<(Socket | null)[]> {
const sockets = wsEndpoints.map((wsEndpoint) => {
if (wsEndpoint === null || wsEndpoint === undefined) {
return null;
}
const origin = new URL(wsEndpoint).origin;
const path = `${new URL(wsEndpoint).pathname}/socket.io/`;
return io(origin, { path });
});
await new Promise((resolve) => {
const timer = setInterval(() => {
for (let i = 0; i < sockets.length; i++) {
const socket = sockets[i];
if (socket === null) continue;
if (!socket.id) return;
}
clearInterval(timer);
resolve(true);
}, 500);
});
return sockets;
}
```
### `setupTSS`
```jsx
async function setupTSS(tssShare: string, pubKey: string, verifierName: string, verifierId: string): Promise<any> {
const endpoints = [tssServerEndpoint, null];
const wsEndpoints = [tssServerEndpoint, null];
const sockets = await createSockets(wsEndpoints);
const parsedTSSShare = {
share: tssShare.split("-")[0].split(":")[1],
index: tssShare.split("-")[1].split(":")[1],
};
const base64Share = Buffer.from(parsedTSSShare.share.padStart(64, "0"), "hex").toString("base64");
// TODO: extend
const localIndex = 1;
const remoteIndex = 0;
const parties = [0, 1];
return new Client(`${verifierName}~${verifierId}:${Date.now()}`, localIndex, parties, endpoints, sockets, base64Share, pubKey, true, tssImportURL);
}
```
### onMount/useEffect
```jsx=
try {
let getTSSData: () => Promise<{
tssShare: string;
signatures: string[];
}>;
const tssGetPublic = async () => {
if (!getTSSData) {
throw new Error("tssShare / sigs are undefined");
}
const { tssShare, signatures } = await getTSSData();
const pubKey = await getPublicKeyFromTSSShare(tssShare, signatures);
return Buffer.from(pubKey, "base64");
};
const clients: { client: any; allocated: boolean }[] = [];
const tssSign = async (msgHash: Buffer) => {
this.finalHash = `0x${msgHash.toString("hex")}`;
let foundClient = null;
while (!foundClient) {
for (let i = 0; i < clients.length; i++) {
const client = clients[i];
if (!client.allocated) {
client.allocated = true;
foundClient = client;
}
}
await new Promise((resolve) => setTimeout(resolve, 1000));
}
await foundClient.client;
await tss.default(tssImportURL);
const { r, s, recoveryParam } = await foundClient.client.sign(tss as any, Buffer.from(msgHash).toString("base64"), true, "", "keccak256");
return { v: recoveryParam + 27, r: Buffer.from(r.toString("hex"), "hex"), s: Buffer.from(s.toString("hex"), "hex") };
};
this.generatePrecompute = async () => {
if (!getTSSData) {
throw new Error("tssShare and signatures are not defined");
}
if (!this.provider) {
throw new Error("not initialized");
}
const { aggregateVerifier: verifierName, verifierId } = await this.web3auth.getUserInfo();
if (!verifierName || !verifierId) {
throw new Error("not logged in, verifier or verifierId undefined");
}
console.log("WHAT IS THIS", verifierName, verifierId);
const { tssShare, signatures } = await getTSSData();
const pubKey = (await tssGetPublic()).toString("base64");
const client = await setupTSS(tssShare, pubKey, verifierName, verifierId);
await tss.default(tssImportURL);
client.precompute(tss as any);
await client.ready;
clients.push({ client, allocated: false });
};
const openloginAdapter = new OpenloginAdapter({
loginSettings: {
mfaLevel: "mandatory",
},
tssSettings: {
useTSS: true,
tssGetPublic,
tssSign,
tssDataCallback: async (tssDataReader) => {
getTSSData = tssDataReader;
},
},
adapterSettings: {
_iframeUrl: "https://mpc-beta.openlogin.com",
network: "development",
clientId,
},
});
(window as any).openloginAdapter = openloginAdapter;
this.web3auth.configureAdapter(openloginAdapter);
// this.subscribeAuthEvents(this.web3auth)
await this.web3auth.initModal();
} catch (error) {
console.log("error", error);
}
```