@funded-labs/plug-controller
Version:
Internet Computer Plug wallet's controller
641 lines (640 loc) • 33.8 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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 */
jest.mock('@funded-labs/dab-js');
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 dab_js_1 = require("@funded-labs/dab-js");
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 Network_1 = require("./modules/NetworkModule/Network");
const constants_1 = require("../utils/account/constants");
const account_2 = require("../utils/account");
const mockSendICP = jest.fn();
jest.mock('../utils/dfx', () => {
return {
createAgent: jest.fn(() => ({})),
createLedgerActor: () => ({
sendICP: mockSendICP,
}),
};
});
const mockedSendToken = jest.fn(({ amount }) => ({
amount,
}));
const mockedTransferNFT = jest.fn();
const mockedICPunk = {
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_1.Principal.from('ogkan-uvha2-mbm2l-isqcz-odcvg-szdx6-qj5tg-ydzjf-qrwe2-lbzwp-7qe'),
desc: 'string',
properties: [],
},
};
const mockdeNFTCollection = {
name: 'IC Punks',
canisterId: 'qcg3w-tyaaa-aaaah-qakea-cai',
standard: 'ICPunk',
tokens: [mockedICPunk],
};
const icpPrice = 4.29;
dab_js_1.getTokenActor.mockReturnValue({
getMetadata: jest.fn(() => ({
fungible: { symbol: 'WTC', decimals: 5, name: 'Wrapped Cycles' },
})),
getBalance: jest.fn(() => ({
value: '1000',
decimals: 8,
})),
send: mockedSendToken,
});
dab_js_1.getCachedUserNFTs.mockReturnValue(Promise.resolve([mockdeNFTCollection]));
dab_js_1.getNFTActor.mockReturnValue({
transfer: mockedTransferNFT,
});
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)
yield 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 { identity } = (0, account_2.createAccountFromMnemonic)(TEST_MNEMONIC, 0);
const testWallet = new PlugWallet_1.default({
name: 'test',
walletId: '0',
orderNumber: 0,
fetch: cross_fetch_1.default,
network: new Network_1.Mainnet({}, cross_fetch_1.default),
type: constants_1.Types.mnemonic,
identity,
});
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.walletId)).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).toEqual(wallet.toJSON());
expect(state.password).toEqual(TEST_PASSWORD);
const mnemonic = yield keyRing.getMnemonic(state.password);
expect(bip39.validateMnemonic(mnemonic)).toEqual(true);
}));
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).toEqual(wallet.toJSON());
const mnemonic = yield keyRing.getMnemonic(state.password);
expect(mnemonic).toEqual(TEST_MNEMONIC);
expect(state.password).toEqual(TEST_PASSWORD);
expect(bip39.validateMnemonic(mnemonic)).toEqual(true);
}));
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({
canisterId: '5ymop-yyaaa-aaaah-qaa4q-cai',
standard: '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);
}));
});
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.walletId);
expect(keyRing.currentWalletId).toEqual(wallet.walletId);
}));
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({
canisterId: '5ymop-yyaaa-aaaah-qaa4q-cai',
standard: 'xtc',
subaccount: '1',
}); // register WTC to other subaccounts
yield keyRing.registerToken({
canisterId: '5ymop-yyaaa-aaaah-qaa4q-cai',
standard: 'xtc',
subaccount: '2',
}); // register WTC
yield keyRing.registerToken({
canisterId: '5ymop-yyaaa-aaaah-qaa4q-cai',
standard: 'xtc',
subaccount: '2',
}); // register WTC twice
}));
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({ canisterId: 'test', standard: 'xtc' })).rejects.toEqual(new Error(errors_1.ERRORS.INVALID_CANISTER_ID));
yield expect(() => keyRing.registerToken({
canisterId: 'ogkan-uvha2-mbm2l-isqcz-odcvg-szdx6-qj5tg-ydzjf-qrwe2-lbzwp-7qe',
standard: 'xtc',
})).rejects.toEqual(new Error(errors_1.ERRORS.INVALID_CANISTER_ID));
}));
});
describe('get balance', () => {
const balances = {
0: [
{
amount: '1000',
token: {
canisterId: 'ryjl3-tyaaa-aaaaa-aaaba-cai',
decimals: 8,
name: 'ICP',
standard: 'ICP',
symbol: 'ICP',
},
},
{
amount: '1000',
token: {
canisterId: 'aanaa-xaaaa-aaaah-aaeiq-cai',
decimals: 12,
name: 'Cycles',
standard: 'XTC',
symbol: 'XTC',
},
},
{
amount: '1000',
token: {
canisterId: 'utozz-siaaa-aaaam-qaaxq-cai',
decimals: 8,
name: 'Wrapped ICP',
standard: 'WICP',
symbol: 'WICP',
},
},
],
};
let walletsCreated = 0;
const mockgetBalances = (wallet) => __awaiter(void 0, void 0, void 0, function* () {
const randomBalance = BigInt(0);
balances[wallet.walletNumber] = randomBalance;
yield jest
.spyOn(wallet, 'getBalances')
.mockReturnValue(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, mockgetBalances);
}));
test('get default balance', () => __awaiter(void 0, void 0, void 0, function* () {
expect(yield keyRing.getBalances()).toMatchObject(balances[0]);
}));
test('get specific balance', () => __awaiter(void 0, void 0, void 0, function* () {
let ind = Math.round(Math.random() * (walletsCreated - 1));
if (ind === 0)
ind++;
expect(yield keyRing.getBalances({ subaccount: 'ind' })).toBe(balances[ind]);
}));
test('get error with invalid wallet numbers', () => __awaiter(void 0, void 0, void 0, function* () {
yield expect(keyRing.getBalances({ subaccount: '-2' })).rejects.toThrow(errors_1.ERRORS.INVALID_WALLET_NUMBER);
yield expect(keyRing.getBalances({ subaccount: '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('get specific transactions', () => __awaiter(void 0, void 0, void 0, function* () {
const ind = Math.round(Math.random() * (walletsCreated - 1));
expect(yield keyRing.getTransactions({ subaccount: 'ind', icpPrice })).toBe(transactions[ind]);
}));
test('get error with invalid wallet numbers', () => __awaiter(void 0, void 0, void 0, function* () {
yield expect(keyRing.getTransactions({ subaccount: '-2', icpPrice })).rejects.toThrow(errors_1.ERRORS.INVALID_WALLET_NUMBER);
yield expect(keyRing.getTransactions({ subaccount: 'walletsCreated + 2', icpPrice })).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: to.toString(),
amount: amount.toString(),
canisterId: tokens_1.TOKENS.ICP.canisterId,
});
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: to.toString(),
amount: amount.toString(),
canisterId: tokens_1.TOKENS.ICP.canisterId,
});
expect(dfx_1.createAgent).toHaveBeenCalled();
expect(mockedSendToken.mock.calls[0][0].amount).toEqual(amount);
expect(mockedSendToken.mock.calls[0][0].to).toEqual(to);
}));
describe('nfts', () => {
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);
}));
it('should fetch NFTs correctly', () => __awaiter(void 0, void 0, void 0, function* () {
const nfts = yield keyRing.getNFTs();
expect(nfts).toEqual([mockdeNFTCollection]);
}));
it('should fail to fetch NFTs on inexistant account', () => __awaiter(void 0, void 0, void 0, function* () {
yield expect(keyRing.getNFTs({ subaccount: '1' })).rejects.toThrow(errors_1.ERRORS.INVALID_WALLET_NUMBER);
}));
it('should transfer an NFT correctly', () => __awaiter(void 0, void 0, void 0, function* () {
const nfts = (yield keyRing.getNFTs()) || [];
const { tokens } = nfts[0];
const to = 'ogkan-uvha2-mbm2l-isqcz-odcvg-szdx6-qj5tg-ydzjf-qrwe2-lbzwp-7qe';
const transferred = yield keyRing.transferNFT({
token: tokens[0],
to,
standard: tokens[0].standard,
});
expect(transferred).toMatchObject([]);
expect(mockedTransferNFT).toHaveBeenCalled();
expect(mockedTransferNFT.mock.calls[0][0].toString()).toEqual(to);
expect(mockedTransferNFT.mock.calls[0][1].toString()).toEqual(tokens[0].index.toString());
}));
});
afterEach(() => {
jest.clearAllMocks();
});
});
});