Skip to content

Commit

Permalink
update addressBook with entries by chainId, add isEns flag (#152)
Browse files Browse the repository at this point in the history
update addressBook with entries by chainId
add isValidEns checking for address book name
  • Loading branch information
jennypollack authored and rekmarks committed Sep 5, 2019
1 parent 77071fd commit d680065
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 68 deletions.
39 changes: 26 additions & 13 deletions src/user/AddressBookController.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isValidAddress, toChecksumAddress } from 'ethereumjs-util';
import { isValidEnsName } from '../util';
import BaseController, { BaseConfig, BaseState } from '../BaseController';

/**
Expand All @@ -23,12 +24,14 @@ export interface ContactEntry {
* @property name - Nickname associated with this address
* @property chainId - Chain id identifies the current chain
* @property memo - User's note about address
* @property isEns - is the entry an ENS name
*/
export interface AddressBookEntry {
address: string;
name: string;
chainId: number;
chainId: string;
memo: string;
isEns: boolean;
}

/**
Expand All @@ -39,7 +42,7 @@ export interface AddressBookEntry {
* @property addressBook - Array of contact entry objects
*/
export interface AddressBookState extends BaseState {
addressBook: { [address: string]: AddressBookEntry };
addressBook: { [chainId: string]: { [address: string]: AddressBookEntry } };
}

/**
Expand Down Expand Up @@ -77,14 +80,19 @@ export class AddressBookController extends BaseController<BaseConfig, AddressBoo
*
* @param address - Recipient address to delete
*/
delete(address: string) {
const normalizedAddress = toChecksumAddress(address);
if (!isValidAddress(normalizedAddress) || this.state.addressBook[normalizedAddress] === undefined) {
delete(chainId: string, address: string) {
address = toChecksumAddress(address);
if (!isValidAddress(address) || !this.state.addressBook[chainId] || !this.state.addressBook[chainId][address]) {
return false;
}

const addressBook: { [address: string]: AddressBookEntry } = Object.assign({}, this.state.addressBook);
delete addressBook[normalizedAddress];
const addressBook = Object.assign({}, this.state.addressBook);
delete addressBook[chainId][address];

if (Object.keys(addressBook[chainId]).length === 0) {
delete addressBook[chainId];
}

this.update({ addressBook });
return true;
}
Expand All @@ -98,19 +106,24 @@ export class AddressBookController extends BaseController<BaseConfig, AddressBoo
* @param memo - User's note about address
* @returns - Boolean indicating if the address was successfully set
*/
set(address: string, name: string, chainId = 1, memo = '') {
set(address: string, name: string, chainId = '1', memo = '') {
address = toChecksumAddress(address);
if (!isValidAddress(address)) {
return false;
}

this.update({
addressBook: {
...this.state.addressBook,
[toChecksumAddress(address)]: {
address,
chainId,
memo,
name
[chainId]: {
...this.state.addressBook[chainId],
[address]: {
address,
chainId,
isEns: isValidEnsName(name),
memo,
name
}
}
}
});
Expand Down
6 changes: 6 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,11 @@ export async function timeoutFetch(url: string, options?: RequestInit, timeout:
]);
}

export function isValidEnsName(ensName: string) {
const regex = /^.{7,}\.(eth|test)$/;
return regex.test(ensName);
}

export default {
BNToHex,
fractionBN,
Expand All @@ -328,6 +333,7 @@ export default {
hexToBN,
hexToText,
isSmartContractCode,
isValidEnsName,
normalizeTransaction,
safelyExecute,
timeoutFetch,
Expand Down
177 changes: 136 additions & 41 deletions tests/AddressBookController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,79 @@ describe('AddressBookController', () => {
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo');
expect(controller.state).toEqual({
addressBook: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: 1,
memo: '',
name: 'foo'
1: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: '1',
isEns: false,
memo: '',
name: 'foo'
}
}
}
});
});

it('should add a contact entry with chainId and memo', () => {
const controller = new AddressBookController();
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo', 1, 'account 1');
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo', '1', 'account 1');
expect(controller.state).toEqual({
addressBook: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: 1,
memo: 'account 1',
name: 'foo'
1: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: '1',
isEns: false,
memo: 'account 1',
name: 'foo'
}
}
}
});
});

it('should add a contact entry with chainId and memo', () => {
const controller = new AddressBookController();
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo', 2, 'account 2');
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo', '2', 'account 2');
expect(controller.state).toEqual({
addressBook: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: 2,
memo: 'account 2',
name: 'foo'
2: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: '2',
isEns: false,
memo: 'account 2',
name: 'foo'
}
}
}
});
});

it('should add multiple contact entries with different chainIds', () => {
const controller = new AddressBookController();
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo', '1', 'account 2');
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo', '2', 'account 2');

expect(controller.state).toEqual({
addressBook: {
1: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: '1',
isEns: false,
memo: 'account 2',
name: 'foo'
}
},
2: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: '2',
isEns: false,
memo: 'account 2',
name: 'foo'
}
}
}
});
Expand All @@ -57,11 +95,14 @@ describe('AddressBookController', () => {
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'bar');
expect(controller.state).toEqual({
addressBook: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: 1,
memo: '',
name: 'bar'
1: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: '1',
isEns: false,
memo: '',
name: 'bar'
}
}
}
});
Expand All @@ -73,25 +114,75 @@ describe('AddressBookController', () => {
expect(controller.state).toEqual({ addressBook: {} });
});

it('should remove a contact entry', () => {
it('should remove one contact entry', () => {
const controller = new AddressBookController();
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo');
controller.delete('0x32Be343B94f860124dC4fEe278FDCBD38C102D88');
controller.delete('1', '0x32Be343B94f860124dC4fEe278FDCBD38C102D88');

expect(controller.state).toEqual({ addressBook: {} });
});

it('should remove a contact entry', () => {
it('should remove only one contact entry', () => {
const controller = new AddressBookController();
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo');
controller.set('0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d', 'bar');
controller.delete('1', '0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d');

expect(controller.state).toEqual({
addressBook: {
1: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: '1',
isEns: false,
memo: '',
name: 'foo'
}
}
}
});
});

it('should add two contact entries with the same chainId', () => {
const controller = new AddressBookController();
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo');
controller.set('0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d', 'bar');
controller.delete('0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d');

expect(controller.state).toEqual({
addressBook: {
1: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: '1',
isEns: false,
memo: '',
name: 'foo'
},
'0xC38bF1aD06ef69F0c04E29DBeB4152B4175f0A8D': {
address: '0xC38bF1aD06ef69F0c04E29DBeB4152B4175f0A8D',
chainId: '1',
isEns: false,
memo: '',
name: 'bar'
}
}
}
});
});

it('should correctly mark ens entries', () => {
const controller = new AddressBookController();
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'metamask.eth');
expect(controller.state).toEqual({
addressBook: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: 1,
memo: '',
name: 'foo'
1: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: '1',
isEns: true,
memo: '',
name: 'metamask.eth'
}
}
}
});
Expand All @@ -118,27 +209,31 @@ describe('AddressBookController', () => {
it('should return true to indicate an address book entry has been deleted', () => {
const controller = new AddressBookController();
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo');
expect(controller.delete('0x32Be343B94f860124dC4fEe278FDCBD38C102D88')).toBeTruthy();
expect(controller.delete('1', '0x32Be343B94f860124dC4fEe278FDCBD38C102D88')).toBeTruthy();
});

it('should return false to indicate an address book entry has NOT been deleted', () => {
const controller = new AddressBookController();
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo');
expect(controller.delete('bar')).toBeFalsy();
expect(controller.delete('1', 'bar')).toBeFalsy();
});

it('should normalize addresses so adding a removing entries work across casings', () => {
it('should normalize addresses so adding and removing entries work across casings', () => {
const controller = new AddressBookController();
controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo');
expect(controller.set('0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d', 'bar')).toBeTruthy();
controller.delete('0xC38BF1AD06EF69F0C04E29DBEB4152B4175F0A8D');
controller.set('0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d', 'bar');

controller.delete('1', '0xC38BF1AD06EF69F0C04E29DBEB4152B4175F0A8D');
expect(controller.state).toEqual({
addressBook: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: 1,
memo: '',
name: 'foo'
1: {
'0x32Be343B94f860124dC4fEe278FDCBD38C102D88': {
address: '0x32Be343B94f860124dC4fEe278FDCBD38C102D88',
chainId: '1',
isEns: false,
memo: '',
name: 'foo'
}
}
}
});
Expand Down
Loading

0 comments on commit d680065

Please sign in to comment.