Skip to main content

MPC

MPC Wallet stands for "Multi-Party Computation" Wallet, a technology designed to improve the security and user convenience of cryptocurrency wallets. It operates by distributing keys and performing encryption tasks jointly by multiple participants. This offers several advantages:

  • In MPC wallets, the private key is divided into multiple pieces and stored in a distributed manner, making it impossible to recover the original private key with only one piece. Even if one key piece is leaked or lost, it can still be restored, preventing the risk of losing the entire asset due to the leak or loss of a single key.

  • It is resistant to hacking or insider threats since it does not rely on a central server. Additionally, it operates on a distributed network, eliminating the need for a single trusted point.

  • It can simplify complex key management procedures like mnemonics, improving user experience.

Wallet Creation

This diagram shows the overall flow required to call the MPC Key generation on the client side. The MPC Key returned in the key generation request enables transactions for the user's wallet.

The devicePassword used in the request is used to encrypt the key piece. The encpvstr received in the successful response of the key creation/recovery API call represents the encrypted KeyShare. The encryptDevicePassword is encrypted and returned by WaaS, forming a pair with the distributed key piece. Even if the client knows the plaintext of the devicePassword, it cannot be decrypted.

warning

The encryptDevicePassword, encpv, and wid returned during MPC Key creation/recovery must be safely stored on the client side.

Key Recovery

If KeyShare is lost or damaged, the MPC Key can be recovered. In case of recovery, a new wallet is not created; rather, it is necessary to recover the KeyShare as it becomes unusable if lost or damaged.

With each recovery, the wid value increases by 1, and only the final wid value can be queried when calling the wallet inquiry API.
Additionally, encpvstr and encryptDevicePassword form a pair, and the client must securely store wid, encpvstr, and encryptDevicePassword.


Below is a sample code to help understand the Wallet create/recover process.

// mpc.ts - WAAS Wallet Create/Recover API Example

import axios from 'axios';
import qs from 'qs';
import { emailLogin } from './login'; // (2)
import { createSecureChannel, encrypt } from './secureChannel'; // (1)

/*
This example assumes normal operation and does not handle errors separately.
Error and exception handling should be applied during implementation.
Example written to build ts to js and run the dist file by configuring package.json.
Refer to the script in package.json.
// ``` json
"scripts": {
"start": "tsc | node dist/index.js",
},
// ```
*/

const WAAS_BASE_URL: string = 'https://dev-api.abcwaas.com';

// Not a mandatory function. Utility function.
function getBaseURL(): string {
const waas_base_url: string = process.env.WAAS_BASE_URL || WAAS_BASE_URL;
return waas_base_url;
}

type getWalletResult = {
uid: string;
wid: number;
sid: string;
pvencstr: string;
encryptDevicePassword: string;
};

async function getWallet(
email: string,
encryptedDevicePassword: string,
channelID: string,
accesssToken: string,
): Promise<getWalletResult> {
/*
Creates a unique MPC wallet for the user.

The user will own one unique wallet, and if a wallet already exists, the existing wallet will be restored.

devicePassword refers to the password for the Key Share of the wallet being created or restored.

GetWalletResult example:
>>> {
"uid": "d5b440b8-469b-4e16-8978-d78b73a09c4e",
"wid": 6,
"sid": "0xbE616d5b24903efc58149f3c7511FeC2085c176e",
"pvencstr": "0x1234567890abcdef",
"encryptDevicePassword": "sEbZRmmOrvmMI83XugEzEVwRpwkBBCeXb4jMq1f8Wao="
}

Args:
email (str): User email
encrypted_device_password (str): devicePassword encrypted with Secure Channel
channel_id (str): Secure channel ID
access_token (str): The wallet user's JWT token

Returns:
GetWalletResult: The user's MPC wallet information

Raises:
HTTPError: If the wallet creation request fails
*/
try {
const urlStr = `${getBaseURL()}/wapi/v2/mpc/wallets`;
const data = qs.stringify({
email: email,
devicePassword: encryptedDevicePassword,
});

const response = await axios.post(urlStr, data, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Bearer ${accesssToken}`,
'Secure-Channel': channelID,
},
});

const wallet: getWalletResult = response.data;
return wallet;
} catch (error) {
if (axios.isAxiosError(error)) {
throw new Error(
`fail to getWallet. stataus code: ${
error.status
}, data: ${JSON.stringify(error.response?.data)}`,
);
}

throw new Error(`fail to getWallet`);
}
}

type walletAccount = {
id: string;
sid: string;
ethAddress: string;
icon: string;
name: string;
signer: string;
pubkey: string;
};

type walletInfo = {
_id: string;
uid: string;
wid: number;
email: string;
accounts: walletAccount[];
favorites: string[];
autoconfirms: string[];
twoFactorEnabled: boolean;
twoFactorResetRetryCount: number;
twoFactorRetryFreezeEndTime: number;
twoFactorFreezeEndTime: number;
};

async function getWalletInfo(accessToken: string): Promise<walletInfo> {
/*
Retrieves the user's MPC wallet.

Args:
access_token (str): The wallet user's JWT token.

WalletInfo example:
>>> {
"_id": "657bdc790b67b600128a865f",
"uid": "d5b440b8-469b-4e16-8978-d78b73a09c4e",
"wid": 6,
"email": "test_0@myabcwallet.com",
"accounts": [
{
"id": "0",
"sid": "0xbE616d5b24903efc58149f3c7511FeC2085c176e",
"ethAddress": "0xbE616d5b24903efc58149f3c7511FeC2085c176e",
"icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4Hs....",
"name": "Account 1",
"signer": "mpc",
"pubkey": "0x025c5d89f60eba1b8fc5c2bd2fa28eefe48f4c950815acd7...."
}
],
"favorites": [],
"autoconfirms": [],
"twoFactorEnabled": false,
"twoFactorResetRetryCount": 0,
"twoFactorRetryFreezeEndTime": 0,
"twoFactorFreezeEndTime": 0
}
*/

try {
const urlStr = `${getBaseURL()}/wapi/v2/mpc/wallets/info`;
const response = await axios.get(urlStr, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});

const walletInfoRes: walletInfo = response.data;

return walletInfoRes;
} catch (error) {
if (axios.isAxiosError(error)) {
throw new Error(
`fail to get walletinfo. status code: ${
error.response?.status
}, data: ${JSON.stringify(error.response?.data)}`,
);
}
throw new Error(`fail to get walletinfo.`);
}
}

export async function mpcScenario() {
const email: string = 'email@email.com'; // User email
const password: string = 'password'; // User password
const clientID: string = 'client id'; // Client ID
const clientSecret: string = 'client secret'; // Client Secret

// Creating Secure Channel
const secureChannelRes = await createSecureChannel();

// The password needs to be encrypted with the Secure Channel.
const encryptedPassword = encrypt(secureChannelRes, password);

// Client ID / Client Secret
const auth = Buffer.from(`${clientID}:${clientSecret}`).toString('base64'); // (3)

// Login
const loginResult = await emailLogin(
email,
encryptedPassword,
secureChannelRes.ChannelID,
auth,
);

// JWT token is generated upon success
console.log(`access token : ${loginResult.accessToken}`);

const devicePassword = 'password'; // (4)
const encryptedDevicePassword = encrypt(secureChannelRes, devicePassword);

const wallet = await getWallet(
email,
encryptedDevicePassword,
secureChannelRes.ChannelID,
loginResult.accessToken,
);
console.log(`wallet uid: ${wallet.uid}`);
console.log(`wallet wid: ${wallet.wid}`);
console.log(`wallet sid: ${wallet.sid}`);

const walletInfo = await getWalletInfo(loginResult.accessToken);
console.log(`wallet uid: ${walletInfo.uid}`);
console.log(`wallet wid: ${walletInfo.wid}`);
console.log(`wallet sid: ${walletInfo.accounts[0].sid}`);
}
  1. 🙋 Getting Started > Secure Channel
  2. 🙋 Getting Started > Login
  3. 🙋 You need Client ID / Client Secret. The Client ID and Client Secret must be base64 encoded.
  4. 🙋 devicePassword is used for key shard encryption. Secure Channel encryption is required.