@safik/fk-plug-controller
Version:
Internet Computer Plug wallet's controller
744 lines (743 loc) • 39.4 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
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 __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable @typescript-eslint/camelcase */
/* eslint-disable camelcase */
const bip39 = __importStar(require("bip39"));
const crypto_js_1 = __importDefault(require("crypto-js"));
const principal_1 = require("@dfinity/principal");
const cross_fetch_1 = __importDefault(require("cross-fetch"));
const _1 = __importDefault(require("."));
const errors_1 = require("../errors");
const PlugWallet_1 = __importDefault(require("../PlugWallet"));
const dfx_1 = require("../utils/dfx");
const mock_1 = __importDefault(require("../utils/storage/mock"));
const account_1 = require("../utils/account");
const tokens_1 = require("../constants/tokens");
const mockSendICP = jest.fn();
jest.mock('../utils/dfx', () => {
return {
createAgent: jest.fn(() => ({})),
createLedgerActor: () => ({
sendICP: mockSendICP,
}),
};
});
jest.mock('../utils/dfx/token', () => {
return {
createTokenActor: () => ({
getMetadata: jest.fn(() => ({
fungible: { symbol: 'WTC', decimals: 5, name: 'Wrapped Cycles' },
})),
}),
};
});
// jest.mock('../utils/dfx/nft', () => {
// return {
// createNFTActor: (): {
// user_tokens: jest.Mock<any, any>;
// data_of: jest.Mock<any, any>;
// transfer_to: jest.Mock<boolean, any>;
// } => ({
// user_tokens: jest.fn(() => [BigInt(10)]),
// data_of: jest.fn(() => ({
// index: BigInt(10),
// canister: 'qcg3w-tyaaa-aaaah-qakea-cai',
// name: 'IC Punk# 10',
// url: 'https://qcg3w-tyaaa-aaaah-qakea-cai.raw.ic0.app/Token/10',
// metadata: {
// index: BigInt(10),
// name: 'IC Punk# 10',
// url: 'https://qcg3w-tyaaa-aaaah-qakea-cai.raw.ic0.app/Token/10',
// owner: Principal.from(
// 'ogkan-uvha2-mbm2l-isqcz-odcvg-szdx6-qj5tg-ydzjf-qrwe2-lbzwp-7qe'
// ),
// desc: 'string',
// properties: [],
// },
// })),
// transfer_to: jest.fn((_, id) => {
// if (id === BigInt(130)) {
// throw new Error(ERRORS.TRANSFER_NFT_ERROR);
// }
// return true;
// }),
// }),
// };
// });
jest.mock('../utils/dfx/token/methods', () => {
return {};
});
const TEST_PASSWORD = 'Somepassword1234';
const TEST_MNEMONIC = bip39.generateMnemonic();
const createManyWallets = (keyRing, mockingMethod) => __awaiter(void 0, void 0, void 0, function* () {
const many = Math.round(Math.random() * 20) + 2;
for (let i = 0; i < many; i += 1) {
const wallet = yield keyRing.createPrincipal(); // eslint-disable-line
if (mockingMethod)
mockingMethod(wallet);
}
return many;
});
const createManyTransactions = () => {
const many = Math.round(Math.random() * 20) + 2;
const transactions = { total: 0, transactions: [] };
for (let i = 0; i < many; i += 1) {
transactions.transactions.push({
timestamp: BigInt(0),
hash: '0',
details: {
from: 'string',
to: 'string',
amount: BigInt(1000),
currency: { symbol: 'ICP', decimals: 8 },
fee: {
amount: BigInt(1000),
currency: { symbol: 'ICP', decimals: 8 },
},
status: 'COMPLETED',
},
type: 'SEND',
caller: 'stirng',
});
transactions.transactions.push({
timestamp: BigInt(0),
hash: '0',
details: {
from: 'string',
to: 'string',
amount: BigInt(1000),
currency: { symbol: 'XTC', decimals: 5 },
fee: {
amount: BigInt(1000),
currency: { symbol: 'XTC', decimals: 5 },
},
status: 'COMPLETED',
},
caller: 'string',
type: 'SEND',
});
}
return transactions;
};
describe('Plug KeyRing', () => {
const testWallet = new PlugWallet_1.default({
name: 'test',
mnemonic: TEST_MNEMONIC,
walletNumber: 0,
fetch: cross_fetch_1.default,
});
let keyRing;
const cleanup = () => __awaiter(void 0, void 0, void 0, function* () {
yield mock_1.default.clear();
keyRing = new _1.default(mock_1.default);
});
beforeAll(cleanup);
beforeEach(cleanup);
afterEach(cleanup);
describe('initialization', () => {
it('should be empty and locked if not initialized', () => __awaiter(void 0, void 0, void 0, function* () {
yield expect(() => keyRing.setCurrentPrincipal(testWallet.walletNumber)).rejects.toEqual(Error(errors_1.ERRORS.NOT_INITIALIZED));
yield expect(() => keyRing.unlock(TEST_PASSWORD)).rejects.toEqual(Error(errors_1.ERRORS.NOT_INITIALIZED));
yield expect(() => keyRing.createPrincipal()).rejects.toEqual(Error(errors_1.ERRORS.NOT_INITIALIZED));
yield expect(() => keyRing.getState()).rejects.toEqual(Error(errors_1.ERRORS.NOT_INITIALIZED));
expect(keyRing.isInitialized).toBe(false);
expect(keyRing.isUnlocked).toBe(false);
}));
});
describe('creation', () => {
it('should create a new keyring and be locked by default', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
expect(keyRing.isInitialized).toBe(true);
expect(keyRing.isUnlocked).toBe(false);
yield expect(() => keyRing.getState()).rejects.toEqual(Error(errors_1.ERRORS.STATE_LOCKED));
}));
it('should create a new keyring and expose state correctly', () => __awaiter(void 0, void 0, void 0, function* () {
const { wallet } = yield keyRing.create({ password: TEST_PASSWORD });
expect(yield keyRing.unlock(TEST_PASSWORD)).toEqual(true);
expect(keyRing.isInitialized).toBe(true);
expect(keyRing.isUnlocked).toBe(true);
const state = yield keyRing.getState();
expect(state.wallets.length).toEqual(1);
const stateWallet = state.wallets[0];
expect(stateWallet.toJSON()).toEqual(wallet.toJSON());
expect(state.password).toEqual(TEST_PASSWORD);
expect(bip39.validateMnemonic(state.mnemonic)).toEqual(true);
expect(stateWallet.registeredTokens).toEqual([tokens_1.TOKENS.XTC]);
}));
it('should fail if not password was provided', () => __awaiter(void 0, void 0, void 0, function* () {
yield expect(() => keyRing.create({ password: '' })).rejects.toEqual(Error(errors_1.ERRORS.PASSWORD_REQUIRED));
}));
});
describe('import', () => {
it('should import a keyring and expose state correctly', () => __awaiter(void 0, void 0, void 0, function* () {
const { wallet } = yield keyRing.importMnemonic({
password: TEST_PASSWORD,
mnemonic: TEST_MNEMONIC,
});
expect(keyRing.isInitialized).toBe(true);
const unlocked = yield keyRing.unlock(TEST_PASSWORD);
expect(keyRing.isUnlocked).toBe(true);
expect(unlocked).toEqual(true);
const state = yield keyRing.getState();
expect(state.wallets.length).toEqual(1);
const stateWallet = state.wallets[0];
expect(stateWallet.toJSON()).toEqual(wallet.toJSON());
expect(state.mnemonic).toEqual(TEST_MNEMONIC);
expect(state.password).toEqual(TEST_PASSWORD);
expect(bip39.validateMnemonic(state.mnemonic)).toEqual(true);
expect(stateWallet.registeredTokens).toEqual([tokens_1.TOKENS.XTC]);
expect(stateWallet.connectedApps).toEqual([]);
}));
it('should fail if not password or mnemonic were provided', () => __awaiter(void 0, void 0, void 0, function* () {
yield expect(() => keyRing.importMnemonic({ password: '', mnemonic: TEST_MNEMONIC })).rejects.toEqual(Error(errors_1.ERRORS.PASSWORD_REQUIRED));
yield expect(() => keyRing.importMnemonic({ password: TEST_PASSWORD, mnemonic: '' })).rejects.toEqual(Error(errors_1.ERRORS.INVALID_MNEMONIC));
expect(keyRing.isInitialized).toBe(false);
}));
it('should fail if the mnemonic is invalid', () => __awaiter(void 0, void 0, void 0, function* () {
yield expect(() => keyRing.importMnemonic({
password: TEST_PASSWORD,
mnemonic: 'some test mnemonic',
})).rejects.toEqual(Error(errors_1.ERRORS.INVALID_MNEMONIC));
expect(keyRing.isInitialized).toBe(false);
}));
it('should import the same wallet even with different passwords', () => __awaiter(void 0, void 0, void 0, function* () {
const { wallet } = yield keyRing.importMnemonic({
mnemonic: TEST_MNEMONIC,
password: TEST_PASSWORD,
});
const { wallet: newWallet } = yield keyRing.importMnemonic({
mnemonic: TEST_MNEMONIC,
password: 'newpassword1',
});
expect(wallet.toJSON()).toEqual(newWallet.toJSON());
expect(wallet.principal).toEqual(newWallet.principal);
}));
});
describe('lock', () => {
it('should create a keyring and be locked by default', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield expect(() => keyRing.getState()).rejects.toEqual(Error(errors_1.ERRORS.STATE_LOCKED));
expect(keyRing.isInitialized).toBe(true);
expect(keyRing.isUnlocked).toBe(false);
}));
it('should import a keyring and be locked by default', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.importMnemonic({
password: TEST_PASSWORD,
mnemonic: TEST_MNEMONIC,
});
yield expect(() => keyRing.getState()).rejects.toEqual(Error(errors_1.ERRORS.STATE_LOCKED));
expect(keyRing.isUnlocked).toBe(false);
}));
it('should unlock correctly with correct password', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
expect(keyRing.isUnlocked).toBe(true);
const state = yield keyRing.getState();
expect(state.wallets.length).toEqual(1);
expect(state.password).toEqual(TEST_PASSWORD);
}));
it('should fail to unlock with incorrect password', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
const unlocked = yield keyRing.unlock('false1234');
expect(unlocked).toBe(false);
expect(keyRing.isUnlocked).toBe(false);
yield expect(() => keyRing.getState()).rejects.toEqual(Error(errors_1.ERRORS.STATE_LOCKED));
}));
it('should lock correctly when unlocked', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
expect(keyRing.isUnlocked).toBe(true);
yield keyRing.lock();
expect(keyRing.isUnlocked).toBe(false);
yield expect(() => keyRing.getState()).rejects.toEqual(Error(errors_1.ERRORS.STATE_LOCKED));
}));
});
describe('storage', () => {
it('should persist data encypted correctly after creating a new keyring', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
const _a = yield keyRing.getState(), { currentWalletId } = _a, state = __rest(_a, ["currentWalletId"]);
const encryptedState = crypto_js_1.default.AES.encrypt(JSON.stringify(state), TEST_PASSWORD);
const { vault: stored, isInitialized } = mock_1.default.get();
expect(crypto_js_1.default.AES.decrypt(encryptedState, TEST_PASSWORD).toString(crypto_js_1.default.enc.Utf8)).toEqual(crypto_js_1.default.AES.decrypt(stored, TEST_PASSWORD).toString(crypto_js_1.default.enc.Utf8));
expect(isInitialized).toEqual(true);
}));
it('should persist data encypted correctly after importing a keyring', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.importMnemonic({
mnemonic: TEST_MNEMONIC,
password: TEST_PASSWORD,
});
yield keyRing.unlock(TEST_PASSWORD);
const _b = yield keyRing.getState(), { currentWalletId } = _b, state = __rest(_b, ["currentWalletId"]);
const encryptedState = crypto_js_1.default.AES.encrypt(JSON.stringify(state), TEST_PASSWORD);
const { vault: stored, isInitialized } = mock_1.default.get();
expect(crypto_js_1.default.AES.decrypt(encryptedState, TEST_PASSWORD).toString(crypto_js_1.default.enc.Utf8)).toEqual(crypto_js_1.default.AES.decrypt(stored, TEST_PASSWORD).toString(crypto_js_1.default.enc.Utf8));
expect(isInitialized).toEqual(true);
}));
it('should persist data encypted correctly after creating a new principal', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
yield keyRing.createPrincipal();
const _c = yield keyRing.getState(), { currentWalletId } = _c, state = __rest(_c, ["currentWalletId"]);
const encryptedState = crypto_js_1.default.AES.encrypt(JSON.stringify(state), TEST_PASSWORD);
const { vault: stored, isInitialized } = mock_1.default.get();
expect(crypto_js_1.default.AES.decrypt(encryptedState, TEST_PASSWORD).toString(crypto_js_1.default.enc.Utf8)).toEqual(crypto_js_1.default.AES.decrypt(stored, TEST_PASSWORD).toString(crypto_js_1.default.enc.Utf8));
expect(isInitialized).toEqual(true);
}));
it('should persist data encypted correctly after registering a new token', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
yield keyRing.registerToken('5ymop-yyaaa-aaaah-qaa4q-cai', 'xtc'); // register XTC
const _d = yield keyRing.getState(), { currentWalletId } = _d, state = __rest(_d, ["currentWalletId"]);
const encryptedState = crypto_js_1.default.AES.encrypt(JSON.stringify(state), TEST_PASSWORD);
const { vault: stored, isInitialized } = mock_1.default.get();
expect(crypto_js_1.default.AES.decrypt(encryptedState, TEST_PASSWORD).toString(crypto_js_1.default.enc.Utf8)).toEqual(crypto_js_1.default.AES.decrypt(stored, TEST_PASSWORD).toString(crypto_js_1.default.enc.Utf8));
expect(isInitialized).toEqual(true);
}));
});
it('should persist data encypted correctly after adding a new app', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
yield keyRing.addConnectedApp({
name: 'Chris',
icon: ':smile:',
url: 'dx4k2-mtdzp-qavet-nrazz-4tmro-oii6a-hlrlv-azdys-5j72q-ids2p-cae',
whitelist: [],
});
const _a = yield keyRing.getState(), { currentWalletId } = _a, state = __rest(_a, ["currentWalletId"]);
const encryptedState = crypto_js_1.default.AES.encrypt(JSON.stringify(state), TEST_PASSWORD);
const { vault: stored, isInitialized } = mock_1.default.get();
expect(crypto_js_1.default.AES.decrypt(encryptedState, TEST_PASSWORD).toString(crypto_js_1.default.enc.Utf8)).toEqual(crypto_js_1.default.AES.decrypt(stored, TEST_PASSWORD).toString(crypto_js_1.default.enc.Utf8));
expect(isInitialized).toEqual(true);
}));
describe('principal management', () => {
it('should create new principals correctly when unlocked', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
yield keyRing.createPrincipal();
let state = yield keyRing.getState();
expect(state.wallets.length).toEqual(2);
yield keyRing.createPrincipal();
state = yield keyRing.getState();
expect(state.wallets.length).toEqual(3);
}));
it('should create many new principals correctly', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
yield keyRing.createPrincipal();
yield keyRing.createPrincipal();
yield keyRing.createPrincipal();
yield keyRing.createPrincipal();
const state = yield keyRing.getState();
expect(state.wallets.length).toEqual(5);
}));
it('should fail to create new principals when locked', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield expect(() => keyRing.createPrincipal()).rejects.toEqual(Error(errors_1.ERRORS.STATE_LOCKED));
}));
it('should set the current principal correctly', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
yield keyRing.createPrincipal();
const wallet = yield keyRing.createPrincipal();
yield keyRing.setCurrentPrincipal(wallet.walletNumber);
expect(keyRing.currentWalletId).toEqual(wallet.walletNumber);
expect(keyRing.currentWallet).toEqual(wallet);
}));
it('should fail to set invalid current principal ', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
yield expect(() => keyRing.setCurrentPrincipal(1)).rejects.toEqual(new Error(errors_1.ERRORS.INVALID_WALLET_NUMBER));
yield expect(() => keyRing.setCurrentPrincipal(-2)).rejects.toEqual(new Error(errors_1.ERRORS.INVALID_WALLET_NUMBER));
yield expect(() => keyRing.setCurrentPrincipal(1.2)).rejects.toEqual(new Error(errors_1.ERRORS.INVALID_WALLET_NUMBER));
}));
it('should create new wallets with a default name', () => __awaiter(void 0, void 0, void 0, function* () {
const { wallet } = yield keyRing.create({ password: TEST_PASSWORD });
expect(wallet.name).toEqual('Account 1');
const { wallet: newWallet } = yield keyRing.importMnemonic({
mnemonic: TEST_MNEMONIC,
password: TEST_PASSWORD,
});
expect(newWallet.name).toEqual('Account 1');
}));
it('should create new wallets with a correct name and emoji', () => __awaiter(void 0, void 0, void 0, function* () {
const { wallet } = yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
const wallet2 = yield keyRing.createPrincipal({
name: 'My new wallet',
icon: ':smile:',
}); // TODO: TS does not like emojis, but they are unicode strings
expect(wallet2.name).toEqual('My new wallet');
expect(wallet2.icon).toEqual(':smile:');
const wallet3 = yield keyRing.createPrincipal({
name: 'My third wallet',
}); // TODO: TS does not like emojis, but they are unicode strings
expect(wallet3.name).toEqual('My third wallet');
expect(wallet3.icon).toEqual(wallet.icon);
const wallet4 = yield keyRing.createPrincipal({
icon: ':crying:',
}); // TODO: TS does not like emojis, but they are unicode strings
expect(wallet4.name).toEqual(wallet.name);
expect(wallet4.icon).toEqual(':crying:');
}));
it('should change the wallets name correctly', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
yield keyRing.createPrincipal();
yield keyRing.createPrincipal();
yield keyRing.editPrincipal(0, { name: 'New name1' });
yield keyRing.editPrincipal(1, { name: 'New name2' });
yield keyRing.editPrincipal(2, { name: 'New name3' });
const { wallets } = yield keyRing.getState();
expect(wallets[0].name).toEqual('New name1');
expect(wallets[1].name).toEqual('New name2');
expect(wallets[2].name).toEqual('New name3');
}));
it('should fail to change an invalid wallet', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
yield expect(() => keyRing.editPrincipal(10, { name: 'New name', emoji: 'test' })).rejects.toEqual(Error(errors_1.ERRORS.INVALID_WALLET_NUMBER));
yield expect(() => keyRing.editPrincipal(-1, { name: 'New name', emoji: 'test' })).rejects.toEqual(Error(errors_1.ERRORS.INVALID_WALLET_NUMBER));
yield expect(() => keyRing.editPrincipal(1.231, { name: 'New name', emoji: 'test' })).rejects.toEqual(Error(errors_1.ERRORS.INVALID_WALLET_NUMBER));
}));
it('should change the wallet icon correctly', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
yield keyRing.createPrincipal();
yield keyRing.createPrincipal();
yield keyRing.editPrincipal(0, { emoji: '123' });
yield keyRing.editPrincipal(1, { emoji: 'New emoji2' });
yield keyRing.editPrincipal(2, { emoji: 'New name3' });
const { wallets } = yield keyRing.getState();
expect(wallets[0].icon).toEqual('123');
expect(wallets[1].icon).toEqual('New emoji2');
expect(wallets[2].icon).toEqual('New name3');
}));
// Skipped since color is breaking it
it('should register a token correctly to different subaccounts', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
yield keyRing.createPrincipal();
yield keyRing.createPrincipal();
yield keyRing.registerToken('5ymop-yyaaa-aaaah-qaa4q-cai', 'xtc', 1); // register WTC to other subaccounts
yield keyRing.registerToken('5ymop-yyaaa-aaaah-qaa4q-cai', 'xtc', 2); // register WTC
yield keyRing.registerToken('5ymop-yyaaa-aaaah-qaa4q-cai', 'xtc', 2); // register WTC twice
const { wallets } = yield keyRing.getState();
expect(wallets[0].registeredTokens).toMatchObject([tokens_1.TOKENS.XTC]);
expect(wallets[1].registeredTokens).toMatchObject([
tokens_1.TOKENS.XTC,
tokens_1.TOKENS.WTC,
]);
expect(wallets[2].registeredTokens).toMatchObject([
tokens_1.TOKENS.XTC,
tokens_1.TOKENS.WTC,
]);
}));
test('should fail to register an invalid canister id', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
yield expect(() => keyRing.registerToken('test', 'xtc')).rejects.toEqual(new Error(errors_1.ERRORS.INVALID_CANISTER_ID));
yield expect(() => keyRing.registerToken('ogkan-uvha2-mbm2l-isqcz-odcvg-szdx6-qj5tg-ydzjf-qrwe2-lbzwp-7qe', 'xtc')).rejects.toEqual(new Error(errors_1.ERRORS.INVALID_CANISTER_ID));
}));
test('should fail to add an app with an invalid canister whitelisted', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
yield expect(() => keyRing.addConnectedApp({
name: 'Chris',
icon: ':smile:',
url: 'chris123',
whitelist: [
'ogkan-uvha2-mbm2l-isqcz-odcvg-szdx6-qj5tg-ydzjf-qrwe2-lbzwp-7qe',
],
})).rejects.toEqual(new Error(errors_1.ERRORS.INVALID_APP));
}));
test('should do nothing if app was already added', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
const app = {
name: 'Chris',
icon: ':smile:',
url: 'ogkan-uvha2-mbm2l-isqcz-odcvg-szdx6-qj5tg-ydzjf-qrwe2-lbzwp-7qe',
whitelist: [],
};
const connectedApps = yield keyRing.addConnectedApp(app);
yield keyRing.addConnectedApp(app);
expect(connectedApps).toEqual([app]);
}));
test('should delete correctly a previously saved app', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
const app1 = {
name: 'App1',
icon: ':smile:',
url: 'test123.com',
whitelist: [],
};
const app2 = {
name: 'App2',
icon: ':sad:',
url: 'plugwallet.ooo',
whitelist: [],
};
let connectedApps = yield keyRing.addConnectedApp(app1);
connectedApps = yield keyRing.addConnectedApp(app2);
expect(connectedApps).toEqual([app1, app2]);
connectedApps = yield keyRing.deleteConnectedApp(app1.url);
expect(connectedApps).toEqual([app2]);
connectedApps = yield keyRing.deleteConnectedApp(app2.url);
expect(connectedApps).toEqual([]);
}));
test('should do nothing when trying to delete an unexistant app', () => __awaiter(void 0, void 0, void 0, function* () {
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
const account = {
name: 'Some app',
icon: ':smile:',
url: 'test123.com',
whitelist: [],
};
const connectedApps = yield keyRing.deleteConnectedApp(account.url);
expect(connectedApps).toEqual([]);
}));
});
describe('get balance', () => {
const balances = {};
let walletsCreated = 0;
const mockGetBalance = wallet => {
const randomBalance = BigInt(0);
balances[wallet.walletNumber] = randomBalance;
jest.spyOn(wallet, 'getBalance').mockImplementation(jest.fn(() => {
return Promise.resolve(randomBalance);
}));
};
beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
keyRing = new _1.default(mock_1.default);
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
walletsCreated = yield createManyWallets(keyRing, mockGetBalance);
}));
test('get default balance', () => __awaiter(void 0, void 0, void 0, function* () {
expect(yield keyRing.getBalance()).toBe(balances[0]);
}));
test('get specific balance', () => __awaiter(void 0, void 0, void 0, function* () {
const ind = Math.round(Math.random() * (walletsCreated - 1));
expect(yield keyRing.getBalance(ind)).toBe(balances[ind]);
}));
test('get error with invalid wallet numbers', () => __awaiter(void 0, void 0, void 0, function* () {
yield expect(keyRing.getBalance(-2)).rejects.toThrow(errors_1.ERRORS.INVALID_WALLET_NUMBER);
yield expect(keyRing.getBalance(walletsCreated + 2)).rejects.toThrow(errors_1.ERRORS.INVALID_WALLET_NUMBER);
}));
});
describe('get transactions', () => {
const transactions = {};
let walletsCreated = 0;
const mockGetTransaction = wallet => {
const randomTransactions = createManyTransactions();
transactions[wallet.walletNumber] = randomTransactions;
jest.spyOn(wallet, 'getTransactions').mockImplementation(jest.fn(() => {
return Promise.resolve(randomTransactions);
}));
};
beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
keyRing = new _1.default(mock_1.default);
const { wallet } = yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
mockGetTransaction(wallet);
walletsCreated = yield createManyWallets(keyRing, mockGetTransaction);
}));
// test.only('get default trasnactions', async () => {
// expect(await keyRing.getTransactions()).toBe(transactions[0]);
// });
test('get specific transactions', () => __awaiter(void 0, void 0, void 0, function* () {
const ind = Math.round(Math.random() * (walletsCreated - 1));
expect(yield keyRing.getTransactions(ind)).toBe(transactions[ind]);
}));
test('get error with invalid wallet numbers', () => __awaiter(void 0, void 0, void 0, function* () {
yield expect(keyRing.getTransactions(-2)).rejects.toThrow(errors_1.ERRORS.INVALID_WALLET_NUMBER);
yield expect(keyRing.getTransactions(walletsCreated + 2)).rejects.toThrow(errors_1.ERRORS.INVALID_WALLET_NUMBER);
}));
});
describe('sendICP', () => {
let walletsCreated = 0;
beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
keyRing = new _1.default(mock_1.default);
yield keyRing.create({ password: TEST_PASSWORD });
yield keyRing.unlock(TEST_PASSWORD);
walletsCreated = yield createManyWallets(keyRing);
}));
it('call create agent with secret key', () => __awaiter(void 0, void 0, void 0, function* () {
const { wallets } = yield keyRing.getState();
const amount = BigInt(0);
const ind = Math.round(Math.random() * (walletsCreated - 1));
const to = wallets[ind].principal;
yield keyRing.send(to.toString(), amount.toString());
expect(dfx_1.createAgent).toHaveBeenCalled();
}));
it('call sendICP with to account', () => __awaiter(void 0, void 0, void 0, function* () {
const { wallets } = yield keyRing.getState();
const amount = BigInt(0);
const ind = Math.round(Math.random() * (walletsCreated - 1));
const to = (0, account_1.getAccountId)(principal_1.Principal.fromText(wallets[ind].principal));
yield keyRing.send(to, amount.toString());
expect(dfx_1.createAgent).toHaveBeenCalled();
expect(mockSendICP.mock.calls[0][0].amount.toString()).toEqual(amount.toString());
expect(mockSendICP.mock.calls[0][0].to).toEqual(to);
}));
it('call sendICP with to principal', () => __awaiter(void 0, void 0, void 0, function* () {
const { wallets } = yield keyRing.getState();
const amount = BigInt(0);
const ind = Math.round(Math.random() * (walletsCreated - 1));
const to = wallets[ind].principal;
yield keyRing.send(to.toString(), amount.toString());
expect(dfx_1.createAgent).toHaveBeenCalled();
expect(mockSendICP.mock.calls[0][0].amount.toString()).toEqual(amount.toString());
expect(mockSendICP.mock.calls[0][0].to).toEqual((0, account_1.getAccountId)(principal_1.Principal.fromText(to)));
}));
// describe('nfts', () => {
// beforeEach(async () => {
// keyRing = new PlugKeyRing(store);
// await keyRing.importMnemonic({
// password: TEST_PASSWORD,
// mnemonic: TEST_MNEMONIC,
// });
// await keyRing.unlock(TEST_PASSWORD);
// });
// it('should fetch NFTs correctly', async () => {
// const nfts = await keyRing.getNFTs();
// expect(nfts).toEqual([
// {
// index: BigInt(10),
// canister: 'qcg3w-tyaaa-aaaah-qakea-cai',
// name: 'IC Punk# 10',
// url: 'https://qcg3w-tyaaa-aaaah-qakea-cai.raw.ic0.app/Token/10',
// metadata: {
// index: BigInt(10),
// name: 'IC Punk# 10',
// url: 'https://qcg3w-tyaaa-aaaah-qakea-cai.raw.ic0.app/Token/10',
// owner: Principal.from('ogkan-uvha2-mbm2l-isqcz-odcvg-szdx6-qj5tg-ydzjf-qrwe2-lbzwp-7qe'),
// desc: 'string',
// properties: [],
// }
// },
// ]);
// });
// it('should fail to fetch NFTs on inexistant account', async () => {
// await expect(keyRing.getNFTs(1)).rejects.toThrow(
// ERRORS.INVALID_WALLET_NUMBER
// );
// });
// it('should transfer an ICPunk NFT correctly', async () => {
// const nfts = await keyRing.getNFTs();
// const { tokens } = nfts.icpunks[0];
// const transferred = await keyRing.transferNFT({
// id: tokens[0].index,
// to: 'ogkan-uvha2-mbm2l-isqcz-odcvg-szdx6-qj5tg-ydzjf-qrwe2-lbzwp-7qe',
// });
// expect(transferred).toEqual(true);
// });
// it('should transfer an EXT NFT correctly', async () => {
// const nfts = await keyRing.getNFTs();
// const { tokens } = nfts.ext[0];
// const transferred = await keyRing.transferNFT({
// id: tokens[0].id!,
// to: 'ogkan-uvha2-mbm2l-isqcz-odcvg-szdx6-qj5tg-ydzjf-qrwe2-lbzwp-7qe',
// });
// expect(transferred).toEqual(true);
// });
// it('should fail to transfer NFTs on inexistant account', async () => {
// const nfts = await keyRing.getNFTs();
// const { tokens } = nfts.icpunks[0];
// await expect(
// keyRing.transferNFT({
// subAccount: 1,
// id: tokens[0].index!,
// to:
// 'ogkan-uvha2-mbm2l-isqcz-odcvg-szdx6-qj5tg-ydzjf-qrwe2-lbzwp-7qe',
// })
// ).rejects.toThrow(ERRORS.INVALID_WALLET_NUMBER);
// });
// // TODO: Not sure how to make it fail.
// it('should fail to transfer NFTs that is not owned', async () => {
// await expect(
// keyRing.transferNFT({
// id: BigInt(130),
// to:
// 'ogkan-uvha2-mbm2l-isqcz-odcvg-szdx6-qj5tg-ydzjf-qrwe2-lbzwp-7qe',
// })
// ).rejects.toThrow(ERRORS.TRANSFER_NFT_ERROR);
// });
// it('should fail to transfer NFTs to invalid principal', async () => {
// const nfts = await keyRing.getNFTs();
// const { tokens } = nfts.icpunks[0];
// // Malformed pid
// await expect(
// keyRing.transferNFT({
// id: tokens[0].index!,
// to: 'ogkan-uvha2-mbm2l-isqcz-odcvg-szdx6lbzwp-7qe',
// })
// ).rejects.toThrow(ERRORS.INVALID_PRINCIPAL_ID);
// // Account ID
// await expect(
// keyRing.transferNFT({
// id: tokens[0].index!,
// to:
// '9627c5abbae5b63b3a1b2ad8b6ee85e99e45317c4276c8addb39211ce05d2a59',
// })
// ).rejects.toThrow(ERRORS.INVALID_PRINCIPAL_ID);
// // CONNFIRM WITH IC PUNKS
// // Canister ID
// await expect(
// keyRing.transferNFT({
// id: tokens[0].index!,
// to: '6xisx-7yaaa-aaaah-aagga-cai',
// })
// ).rejects.toThrow(ERRORS.INVALID_PRINCIPAL_ID);
// });
// });
afterEach(() => {
jest.clearAllMocks();
});
});
});