@safik/fk-plug-controller
Version:
Internet Computer Plug wallet's controller
272 lines (271 loc) • 13.9 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 principal_1 = require("@dfinity/principal");
const fk_dab_js_1 = require("@safik/fk-dab-js");
const random_color_1 = __importDefault(require("random-color"));
const errors_1 = require("../errors");
const utils_1 = require("../PlugKeyRing/utils");
const account_1 = require("../utils/account");
const dfx_1 = require("../utils/dfx");
const token_1 = require("../utils/dfx/token");
const rosetta_1 = require("../utils/dfx/history/rosetta");
const tokens_1 = require("../constants/tokens");
const array_1 = require("../utils/array");
const xtcHistory_1 = require("../utils/dfx/history/xtcHistory");
const cap_1 = require("../utils/dfx/history/cap");
class PlugWallet {
constructor({ name, icon, walletNumber, mnemonic, registeredTokens = {}, connectedApps = [], assets = tokens_1.DEFAULT_ASSETS, collections = [], fetch, }) {
// TODO: Make generic when standard is adopted. Just supports ICPunks rn.
this.getNFTs = (refresh) => __awaiter(this, void 0, void 0, function* () {
try {
this.collections = yield (0, fk_dab_js_1.getCachedUserNFTs)({
userPID: this.principal,
refresh,
});
return this.collections;
}
catch (e) {
return null;
}
});
this.transferNFT = (args) => __awaiter(this, void 0, void 0, function* () {
const { token, to } = args;
if (!(0, utils_1.validatePrincipalId)(to)) {
throw new Error(errors_1.ERRORS.INVALID_PRINCIPAL_ID);
}
const { secretKey } = this.identity.getKeyPair();
const agent = yield (0, dfx_1.createAgent)({ secretKey, fetch: this.fetch });
try {
const NFT = (0, fk_dab_js_1.getNFTActor)({
canisterId: token.canister,
agent,
standard: token.standard,
});
yield NFT.transfer(principal_1.Principal.fromText(to), parseInt(token.index.toString(), 10));
// Optimistically update the state
const collections = this.collections.map(col => (Object.assign(Object.assign({}, col), { tokens: col.tokens.filter(tok => tok.id !== token.id) })));
this.collections = collections.filter(col => col.tokens.length);
(0, fk_dab_js_1.getCachedUserNFTs)({ userPID: this.principal, refresh: true }).catch(console.warn);
return this.collections;
}
catch (e) {
throw new Error(errors_1.ERRORS.TRANSFER_NFT_ERROR);
}
});
this.registerToken = (canisterId, standard = 'ext') => __awaiter(this, void 0, void 0, function* () {
if (!(0, utils_1.validateCanisterId)(canisterId)) {
throw new Error(errors_1.ERRORS.INVALID_CANISTER_ID);
}
const { secretKey } = this.identity.getKeyPair();
const agent = yield (0, dfx_1.createAgent)({ secretKey, fetch: this.fetch });
const tokenActor = yield (0, token_1.createTokenActor)(canisterId, agent, standard);
const metadata = yield tokenActor.getMetadata();
if (!('fungible' in metadata)) {
throw new Error(errors_1.ERRORS.NON_FUNGIBLE_TOKEN_NOT_SUPPORTED);
}
const color = (0, random_color_1.default)({ luminosity: 'light' });
const tokenDescriptor = Object.assign(Object.assign({}, metadata.fungible), { canisterId,
color,
standard });
const newTokens = Object.assign(Object.assign({}, this.registeredTokens), { [canisterId]: tokenDescriptor });
// const unique = uniqueByObjKey(newTokens, 'symbol') as StandardToken[];
this.registeredTokens = newTokens;
return Object.values(newTokens);
});
this.toJSON = () => ({
name: this.name,
walletNumber: this.walletNumber,
principal: this.identity.getPrincipal().toText(),
accountId: this.accountId,
icon: this.icon,
registeredTokens: this.registeredTokens,
connectedApps: this.connectedApps,
assets: this.assets.map(asset => (Object.assign(Object.assign({}, asset), { amount: asset.amount.toString() }))),
nftCollections: this.collections.map(collection => (Object.assign(Object.assign({}, collection), { tokens: collection.tokens.map(token => (Object.assign(Object.assign({}, token), { index: parseInt(token.index.toString(), 10) }))) }))),
});
this.burnXTC = (to, amount) => __awaiter(this, void 0, void 0, function* () {
if (!(0, utils_1.validateCanisterId)(to)) {
throw new Error(errors_1.ERRORS.INVALID_CANISTER_ID);
}
const { secretKey } = this.identity.getKeyPair();
const agent = yield (0, dfx_1.createAgent)({ secretKey, fetch: this.fetch });
const xtcActor = yield (0, token_1.createTokenActor)(tokens_1.TOKENS.XTC.canisterId, agent, 'xtc');
const burnResult = yield xtcActor.burnXTC({
to: principal_1.Principal.fromText(to),
amount,
});
try {
if ('Ok' in burnResult) {
const trxId = burnResult.Ok;
yield (0, xtcHistory_1.requestCacheUpdate)(this.principal, [trxId]);
}
}
catch (e) {
console.log('Kyasshu error');
}
return burnResult;
});
this.getBalance = () => __awaiter(this, void 0, void 0, function* () {
const { secretKey } = this.identity.getKeyPair();
// Get ICP Balance
const agent = yield (0, dfx_1.createAgent)({ secretKey, fetch: this.fetch });
const ledger = yield (0, dfx_1.createLedgerActor)(agent);
const icpBalance = yield ledger.getBalance(this.accountId);
// Add XTC if it was not in the first place (backwards compatibility)
this.registeredTokens[tokens_1.TOKENS.XTC.canisterId] = tokens_1.TOKENS.XTC;
// Get Custom Token Balances
const tokenBalances = yield Promise.all(Object.values(this.registeredTokens).map((token) => __awaiter(this, void 0, void 0, function* () {
const tokenActor = yield (0, token_1.createTokenActor)(token.canisterId, agent, token.standard);
const balance = yield tokenActor.getBalance(this.identity.getPrincipal());
return {
name: token.name,
symbol: token.symbol,
amount: (0, token_1.parseBalance)(balance),
canisterId: token.canisterId,
};
})));
const assets = [
{
name: 'ICP',
symbol: 'ICP',
amount: (0, token_1.parseBalance)(icpBalance),
canisterId: null,
},
...tokenBalances,
];
this.assets = assets;
return assets;
});
this.getTokenInfo = (canisterId, standard = 'ext') => __awaiter(this, void 0, void 0, function* () {
var _a;
const { secretKey } = this.identity.getKeyPair();
if (!(0, utils_1.validateCanisterId)(canisterId)) {
throw new Error(errors_1.ERRORS.INVALID_CANISTER_ID);
}
const agent = yield (0, dfx_1.createAgent)({ secretKey, fetch: this.fetch });
const savedStandard = ((_a = this.registeredTokens[canisterId]) === null || _a === void 0 ? void 0 : _a.standard) || standard;
const tokenActor = yield (0, token_1.createTokenActor)(canisterId, agent, savedStandard);
const metadataResult = yield tokenActor.getMetadata();
const metadata = metadataResult;
if (!('fungible' in metadata)) {
throw new Error(errors_1.ERRORS.NON_FUNGIBLE_TOKEN_NOT_SUPPORTED);
}
const tokenBalance = yield tokenActor.getBalance(this.identity.getPrincipal());
const token = Object.assign(Object.assign({}, metadata.fungible), { canisterId, standard: savedStandard });
return { token, amount: (0, token_1.parseBalance)(tokenBalance) };
});
this.getTransactions = () => __awaiter(this, void 0, void 0, function* () {
const icpTrxs = yield (0, rosetta_1.getICPTransactions)(this.accountId);
const xtcTransactions = yield (0, xtcHistory_1.getXTCTransactions)(this.principal);
const capTransactions = yield (0, cap_1.getCapTransactions)(this.principal);
// merge and format all trx. sort by timestamp
// TODO: any custom token impelmenting archive should be queried. (0.4.0)
const transactions = {
total: icpTrxs.total + xtcTransactions.total + capTransactions.total,
transactions: [
...capTransactions.transactions,
...icpTrxs.transactions,
...xtcTransactions.transactions,
].sort((a, b) => (b.timestamp - a.timestamp < 0 ? -1 : 1)),
};
return transactions;
});
this.send = (to, amount, canisterId, opts) => __awaiter(this, void 0, void 0, function* () {
return canisterId
? this.sendCustomToken(to, amount, canisterId)
: { height: yield this.sendICP(to, amount, opts) };
});
this.addConnectedApp = (app) => {
if (!app.url ||
!app.name ||
!app.icon ||
!app.whitelist.every(item => (0, utils_1.validateCanisterId)(item))) {
throw new Error(errors_1.ERRORS.INVALID_APP);
}
this.connectedApps = (0, array_1.uniqueByObjKey)([...this.connectedApps, app], 'url');
return this.connectedApps;
};
this.deleteConnectedApp = (url) => {
if (!this.connectedApps.some(app => app.url === url)) {
return this.connectedApps;
}
this.connectedApps = [...this.connectedApps.filter(app => app.url !== url)];
return this.connectedApps;
};
this.name = name || 'Account 1';
this.icon = icon;
this.walletNumber = walletNumber;
this.assets = assets;
this.registeredTokens = (0, token_1.formatStorageTokens)(Object.assign(Object.assign({}, registeredTokens), { [tokens_1.TOKENS.XTC.canisterId]: tokens_1.TOKENS.XTC }));
const { identity, accountId } = (0, account_1.createAccountFromMnemonic)(mnemonic, walletNumber);
this.identity = identity;
this.accountId = accountId;
this.principal = identity.getPrincipal().toText();
this.connectedApps = [...connectedApps];
this.collections = [...collections];
this.fetch = fetch;
}
setName(val) {
this.name = val;
}
sign(payload) {
return __awaiter(this, void 0, void 0, function* () {
return this.identity.sign(payload);
});
}
setIcon(val) {
this.icon = val;
}
get publicKey() {
return this.identity.getKeyPair().publicKey;
}
get pemFile() {
return this.identity.getPem();
}
sendICP(to, amount, opts) {
return __awaiter(this, void 0, void 0, function* () {
const { secretKey } = this.identity.getKeyPair();
const agent = yield (0, dfx_1.createAgent)({ secretKey, fetch: this.fetch });
const ledger = yield (0, dfx_1.createLedgerActor)(agent);
return ledger.sendICP({ to, amount, opts });
});
}
sendCustomToken(to, amount, canisterId) {
return __awaiter(this, void 0, void 0, function* () {
const { secretKey } = this.identity.getKeyPair();
const agent = yield (0, dfx_1.createAgent)({ secretKey, fetch: this.fetch });
const savedToken = this.registeredTokens[canisterId];
const tokenActor = yield (0, token_1.createTokenActor)(canisterId, agent, savedToken.standard);
const result = yield tokenActor.send({
to,
from: this.identity.getPrincipal().toString(),
amount,
});
if (canisterId === tokens_1.TOKENS.XTC.canisterId) {
try {
if ('transactionId' in result) {
const trxId = result.transactionId;
yield (0, xtcHistory_1.requestCacheUpdate)(this.principal, [BigInt(trxId)]);
}
}
catch (e) {
console.log('Kyasshu error', e);
}
}
return result;
});
}
}
exports.default = PlugWallet;