@safik/fk-plug-controller
Version:
Internet Computer Plug wallet's controller
289 lines (288 loc) • 15.1 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const crypto_js_1 = __importDefault(require("crypto-js"));
const principal_1 = require("@dfinity/principal");
const json_bigint_1 = __importDefault(require("json-bigint"));
const errors_1 = require("../errors");
const PlugWallet_1 = __importDefault(require("../PlugWallet"));
const account_1 = require("../utils/account");
const storage_1 = __importDefault(require("../utils/storage"));
const utils_1 = require("./utils");
const object_1 = require("../utils/object");
class PlugKeyRing {
constructor(StorageAdapter = new storage_1.default(), CryptoAdapter = crypto_js_1.default, FetchAdapter) {
this.isUnlocked = false;
this.isInitialized = false;
this.init = () => __awaiter(this, void 0, void 0, function* () {
const state = (yield this.storage.get());
this.isUnlocked = !!(state === null || state === void 0 ? void 0 : state.isUnlocked);
this.isInitialized = !!(state === null || state === void 0 ? void 0 : state.isInitialized);
this.currentWalletId = (state === null || state === void 0 ? void 0 : state.currentWalletId) || 0;
});
this.getPublicKey = (subAccount) => __awaiter(this, void 0, void 0, function* () {
yield this.checkInitialized();
const index = (subAccount !== null && subAccount !== void 0 ? subAccount : this.currentWalletId) || 0;
this.validateSubaccount(index);
const wallet = this.state.wallets[index];
return wallet.publicKey;
});
this.getNFTs = (subAccount) => __awaiter(this, void 0, void 0, function* () {
this.checkUnlocked();
const index = (subAccount !== null && subAccount !== void 0 ? subAccount : this.currentWalletId) || 0;
const { wallets } = this.state;
this.validateSubaccount(index);
const wallet = wallets[index];
const nfts = yield wallet.getNFTs();
wallets.splice(index, 1, wallet);
this.state.wallets = wallets;
yield this.saveEncryptedState({ wallets }, this.state.password);
return nfts;
});
this.transferNFT = ({ subAccount, token, to, }) => __awaiter(this, void 0, void 0, function* () {
this.checkUnlocked();
const index = (subAccount !== null && subAccount !== void 0 ? subAccount : this.currentWalletId) || 0;
const { wallets } = this.state;
this.validateSubaccount(index);
const wallet = wallets[index];
const collections = yield wallet.transferNFT({ token, to });
wallets.splice(index, 1, wallet);
this.state.wallets = wallets;
yield this.saveEncryptedState({ wallets }, this.state.password);
return collections;
});
this.loadFromPersistance = (password) => __awaiter(this, void 0, void 0, function* () {
const { vault, isInitialized, currentWalletId, } = ((yield this.storage.get()) || {});
if (isInitialized && vault) {
const decrypted = this.decryptState(vault, password);
const wallets = decrypted.wallets.map(wallet => new PlugWallet_1.default(Object.assign(Object.assign({}, wallet), { mnemonic: decrypted.mnemonic, fetch: this.fetch })));
this.state = Object.assign(Object.assign({}, decrypted), { wallets });
this.isInitialized = isInitialized;
this.currentWalletId = currentWalletId;
}
});
this.burnXTC = ({ to, amount, subAccount, }) => __awaiter(this, void 0, void 0, function* () {
this.checkUnlocked();
const index = (subAccount !== null && subAccount !== void 0 ? subAccount : this.currentWalletId) || 0;
const { wallets } = this.state;
this.validateSubaccount(index);
const wallet = wallets[index];
return wallet.burnXTC(to, amount);
});
this.create = ({ password = '', icon, name, entropy, }) => __awaiter(this, void 0, void 0, function* () {
const { mnemonic } = (0, account_1.createAccount)(entropy);
const wallet = yield this.createAndPersistKeyRing({
mnemonic,
password,
icon,
name,
});
return { wallet, mnemonic };
});
this.importMnemonic = ({ mnemonic, password, }) => __awaiter(this, void 0, void 0, function* () {
const wallet = yield this.createAndPersistKeyRing({ mnemonic, password });
return { wallet, mnemonic };
});
// Assumes the state is already initialized
this.createPrincipal = (opts) => __awaiter(this, void 0, void 0, function* () {
yield this.checkInitialized();
this.checkUnlocked();
const wallet = new PlugWallet_1.default(Object.assign(Object.assign({}, opts), { mnemonic: this.state.mnemonic, walletNumber: this.state.wallets.length, fetch: this.fetch }));
const wallets = [...this.state.wallets, wallet];
yield this.saveEncryptedState({ wallets }, this.state.password);
this.state.wallets = wallets;
return wallet;
});
this.setCurrentPrincipal = (walletNumber) => __awaiter(this, void 0, void 0, function* () {
yield this.checkInitialized();
this.validateSubaccount(walletNumber);
this.currentWalletId = walletNumber;
yield this.storage.set({ currentWalletId: walletNumber });
});
this.getState = () => __awaiter(this, void 0, void 0, function* () {
yield this.checkInitialized();
this.checkUnlocked();
return (0, object_1.recursiveParseBigint)(Object.assign(Object.assign({}, this.state), { currentWalletId: this.currentWalletId }));
});
this.sign = (payload, subAccount) => __awaiter(this, void 0, void 0, function* () {
this.checkUnlocked();
const index = (subAccount !== null && subAccount !== void 0 ? subAccount : this.currentWalletId) || 0;
this.validateSubaccount(index);
const wallet = this.state.wallets[index];
const signed = yield wallet.sign(payload);
return signed;
});
this.unlock = (password) => __awaiter(this, void 0, void 0, function* () {
var _a;
yield this.checkInitialized();
try {
yield this.loadFromPersistance(password);
this.isUnlocked = password === ((_a = this.state) === null || _a === void 0 ? void 0 : _a.password);
yield this.storage.set({ isUnlocked: this.isUnlocked });
return this.isUnlocked;
}
catch (e) {
this.isUnlocked = false;
return false;
}
});
this.lock = () => __awaiter(this, void 0, void 0, function* () {
this.isUnlocked = false;
this.state = { wallets: [] };
yield this.storage.set({ isUnlocked: this.isUnlocked });
});
this.editPrincipal = (walletNumber, { name, emoji }) => __awaiter(this, void 0, void 0, function* () {
yield this.checkInitialized();
this.checkUnlocked();
this.validateSubaccount(walletNumber);
const wallet = this.state.wallets[walletNumber];
if (name)
wallet.setName(name);
if (emoji)
wallet.setIcon(emoji);
const { wallets } = this.state;
wallets.splice(walletNumber, 1, wallet);
this.state.wallets = wallets;
this.saveEncryptedState({ wallets }, this.state.password);
});
this.registerToken = (canisterId, standard = 'ext', subAccount) => __awaiter(this, void 0, void 0, function* () {
this.checkUnlocked();
const index = (subAccount !== null && subAccount !== void 0 ? subAccount : this.currentWalletId) || 0;
const { wallets } = this.state;
this.validateSubaccount(index);
const wallet = wallets[index];
const registeredTokens = yield wallet.registerToken(canisterId, standard);
wallets.splice(index, 1, wallet);
this.state.wallets = wallets;
yield this.saveEncryptedState({ wallets }, this.state.password);
return registeredTokens;
});
this.getBalance = (subAccount) => __awaiter(this, void 0, void 0, function* () {
this.checkUnlocked();
const index = (subAccount !== null && subAccount !== void 0 ? subAccount : this.currentWalletId) || 0;
this.validateSubaccount(index);
return this.state.wallets[index].getBalance();
});
this.getTokenInfo = (canisterId, standard = 'ext', subAccount) => __awaiter(this, void 0, void 0, function* () {
this.checkUnlocked();
const index = (subAccount !== null && subAccount !== void 0 ? subAccount : this.currentWalletId) || 0;
this.validateSubaccount(index);
return this.state.wallets[index].getTokenInfo(canisterId, standard);
});
this.getTransactions = (subAccount) => __awaiter(this, void 0, void 0, function* () {
this.checkUnlocked();
const index = (subAccount !== null && subAccount !== void 0 ? subAccount : this.currentWalletId) || 0;
this.validateSubaccount(index);
return this.state.wallets[index].getTransactions();
});
this.checkInitialized = () => __awaiter(this, void 0, void 0, function* () {
yield this.init();
if (!this.isInitialized)
throw new Error(errors_1.ERRORS.NOT_INITIALIZED);
});
this.send = (to, amount, canisterId, opts) => __awaiter(this, void 0, void 0, function* () {
this.checkUnlocked();
const currentWalletNumber = this.currentWalletId;
let account = to;
if (!canisterId && (0, utils_1.validatePrincipalId)(to)) {
account = (0, account_1.getAccountId)(principal_1.Principal.fromText(to));
}
return this.state.wallets[currentWalletNumber || 0].send(account, amount, canisterId, opts);
});
this.addConnectedApp = (app, subAccount) => __awaiter(this, void 0, void 0, function* () {
this.checkUnlocked();
const index = (subAccount !== null && subAccount !== void 0 ? subAccount : this.currentWalletId) || 0;
this.validateSubaccount(index);
const { wallets } = this.state;
const wallet = wallets[index];
const apps = wallet.addConnectedApp(app);
this.state.wallets = wallets;
yield this.saveEncryptedState({ wallets }, this.state.password);
return apps;
});
this.deleteConnectedApp = (id, subAccount) => __awaiter(this, void 0, void 0, function* () {
this.checkUnlocked();
const index = (subAccount !== null && subAccount !== void 0 ? subAccount : this.currentWalletId) || 0;
this.validateSubaccount(index);
const { wallets } = this.state;
const wallet = wallets[index];
const apps = wallet.deleteConnectedApp(id);
this.state.wallets = wallets;
yield this.saveEncryptedState({ wallets }, this.state.password);
return apps;
});
this.getPemFile = (walletNumber) => {
this.checkUnlocked();
const currentWalletNumber = (walletNumber !== null && walletNumber !== void 0 ? walletNumber : this.currentWalletId) || 0;
this.validateSubaccount(currentWalletNumber);
return this.state.wallets[currentWalletNumber].pemFile;
};
this.checkUnlocked = () => {
if (!this.isUnlocked) {
throw new Error(errors_1.ERRORS.STATE_LOCKED);
}
};
this.createAndPersistKeyRing = ({ mnemonic, password, icon, name, }) => __awaiter(this, void 0, void 0, function* () {
if (!password)
throw new Error(errors_1.ERRORS.PASSWORD_REQUIRED);
const wallet = new PlugWallet_1.default({
icon,
name,
mnemonic,
walletNumber: 0,
fetch: this.fetch,
});
const data = {
wallets: [wallet.toJSON()],
password,
mnemonic,
};
this.isInitialized = true;
this.currentWalletId = 0;
yield this.storage.clear();
yield this.storage.set({
isInitialized: true,
isUnlocked: false,
currentWalletId: 0,
});
yield this.saveEncryptedState(data, password);
return wallet;
});
this.saveEncryptedState = (newState, password) => __awaiter(this, void 0, void 0, function* () {
const stringData = json_bigint_1.default.stringify(Object.assign(Object.assign({}, this.state), newState));
const encrypted = this.crypto.AES.encrypt(stringData, password);
yield this.storage.set({ vault: encrypted.toString() });
});
this.decryptState = (state, password) => JSON.parse(this.crypto.AES.decrypt(state, password).toString(this.crypto.enc.Utf8));
this.state = { wallets: [] };
this.isUnlocked = false;
this.isInitialized = false;
this.currentWalletId = 0;
this.storage = StorageAdapter;
this.crypto = CryptoAdapter;
this.fetch = FetchAdapter;
}
validateSubaccount(subaccount) {
if (subaccount < 0 ||
!Number.isInteger(subaccount) ||
subaccount >= (this.state.wallets.length || 0)) {
throw new Error(errors_1.ERRORS.INVALID_WALLET_NUMBER);
}
}
get currentWallet() {
this.checkUnlocked();
return this.state.wallets[this.currentWalletId || 0];
}
}
exports.default = PlugKeyRing;