Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Fantom Opera chain #10487

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,9 @@
"connectingTo": {
"message": "Connecting to $1"
},
"connectingToFantom": {
"message": "Connecting to Fantom Opera"
},
"connectingToGoerli": {
"message": "Connecting to Goerli Test Network"
},
Expand Down Expand Up @@ -666,6 +669,9 @@
"failureMessage": {
"message": "Something went wrong, and we were unable to complete the action"
},
"fantom": {
"message": "Fantom Opera"
},
"fast": {
"message": "Fast"
},
Expand Down
12 changes: 9 additions & 3 deletions app/scripts/controllers/network/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ import EthQuery from 'eth-query';
import {
RINKEBY,
MAINNET,
FANTOM,
FANTOM_RPC,
INFURA_PROVIDER_TYPES,
NETWORK_TYPE_RPC,
NETWORK_TYPE_TO_ID_MAP,
MAINNET_CHAIN_ID,
RINKEBY_CHAIN_ID,
FANTOM_CHAIN_ID,
} from '../../../../shared/constants/network';
import {
isPrefixedFormattedHexString,
Expand Down Expand Up @@ -189,16 +192,17 @@ export default class NetworkController extends EventEmitter {
});
}

async setProviderType(type, rpcUrl = '', ticker = 'ETH', nickname = '') {
async setProviderType(type, rpcUrl = '', tickerParam = null, nickname = '') {
assert.notStrictEqual(
type,
NETWORK_TYPE_RPC,
`NetworkController - cannot call "setProviderType" with type "${NETWORK_TYPE_RPC}". Use "setRpcTarget"`,
);
assert.ok(
INFURA_PROVIDER_TYPES.includes(type),
`Unknown Infura provider type "${type}".`,
INFURA_PROVIDER_TYPES.includes(type) || type === FANTOM,
`Unknown built-in provider type "${type}".`,
);
const ticker = tickerParam || (type === FANTOM ? 'FTM' : 'ETH');
const { chainId } = NETWORK_TYPE_TO_ID_MAP[type];
this.setProviderConfig({ type, rpcUrl, chainId, ticker, nickname });
}
Expand Down Expand Up @@ -247,6 +251,8 @@ export default class NetworkController extends EventEmitter {
const isInfura = INFURA_PROVIDER_TYPES.includes(type);
if (isInfura) {
this._configureInfuraProvider(type, this._infuraProjectId);
} else if (type === FANTOM) {
this._configureStandardProvider(FANTOM_RPC, FANTOM_CHAIN_ID);
// url-based rpc endpoints
} else if (type === NETWORK_TYPE_RPC) {
this._configureStandardProvider(rpcUrl, chainId);
Expand Down
9 changes: 9 additions & 0 deletions shared/constants/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ export const RINKEBY = 'rinkeby';
export const KOVAN = 'kovan';
export const MAINNET = 'mainnet';
export const GOERLI = 'goerli';
export const FANTOM = 'fantom';
export const NETWORK_TYPE_RPC = 'rpc';

export const MAINNET_NETWORK_ID = '1';
export const ROPSTEN_NETWORK_ID = '3';
export const RINKEBY_NETWORK_ID = '4';
export const GOERLI_NETWORK_ID = '5';
export const KOVAN_NETWORK_ID = '42';
export const FANTOM_NETWORK_ID = '250';

export const MAINNET_CHAIN_ID = '0x1';
export const ROPSTEN_CHAIN_ID = '0x3';
export const RINKEBY_CHAIN_ID = '0x4';
export const GOERLI_CHAIN_ID = '0x5';
export const KOVAN_CHAIN_ID = '0x2a';
export const FANTOM_CHAIN_ID = '0xfa';

/**
* The largest possible chain ID we can handle.
Expand All @@ -28,8 +31,10 @@ export const RINKEBY_DISPLAY_NAME = 'Rinkeby';
export const KOVAN_DISPLAY_NAME = 'Kovan';
export const MAINNET_DISPLAY_NAME = 'Ethereum Mainnet';
export const GOERLI_DISPLAY_NAME = 'Goerli';
export const FANTOM_DISPLAY_NAME = 'Fantom Opera';

export const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET, GOERLI];
export const FANTOM_RPC = 'https://rpcapi.fantom.network';

export const TEST_CHAINS = [
ROPSTEN_CHAIN_ID,
Expand All @@ -44,6 +49,7 @@ export const NETWORK_TYPE_TO_ID_MAP = {
[KOVAN]: { networkId: KOVAN_NETWORK_ID, chainId: KOVAN_CHAIN_ID },
[GOERLI]: { networkId: GOERLI_NETWORK_ID, chainId: GOERLI_CHAIN_ID },
[MAINNET]: { networkId: MAINNET_NETWORK_ID, chainId: MAINNET_CHAIN_ID },
[FANTOM]: { networkId: FANTOM_NETWORK_ID, chainId: FANTOM_CHAIN_ID },
};

export const NETWORK_TO_NAME_MAP = {
Expand All @@ -52,18 +58,21 @@ export const NETWORK_TO_NAME_MAP = {
[KOVAN]: KOVAN_DISPLAY_NAME,
[MAINNET]: MAINNET_DISPLAY_NAME,
[GOERLI]: GOERLI_DISPLAY_NAME,
[FANTOM]: FANTOM_DISPLAY_NAME,

[ROPSTEN_NETWORK_ID]: ROPSTEN_DISPLAY_NAME,
[RINKEBY_NETWORK_ID]: RINKEBY_DISPLAY_NAME,
[KOVAN_NETWORK_ID]: KOVAN_DISPLAY_NAME,
[GOERLI_NETWORK_ID]: GOERLI_DISPLAY_NAME,
[MAINNET_NETWORK_ID]: MAINNET_DISPLAY_NAME,
[FANTOM_NETWORK_ID]: FANTOM_DISPLAY_NAME,

[ROPSTEN_CHAIN_ID]: ROPSTEN_DISPLAY_NAME,
[RINKEBY_CHAIN_ID]: RINKEBY_DISPLAY_NAME,
[KOVAN_CHAIN_ID]: KOVAN_DISPLAY_NAME,
[GOERLI_CHAIN_ID]: GOERLI_DISPLAY_NAME,
[MAINNET_CHAIN_ID]: MAINNET_DISPLAY_NAME,
[FANTOM_CHAIN_ID]: FANTOM_DISPLAY_NAME,
};

export const CHAIN_ID_TO_TYPE_MAP = Object.entries(
Expand Down
3 changes: 3 additions & 0 deletions ui/app/components/app/dropdowns/network-dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ class NetworkDropdown extends Component {
name = this.context.t('rinkeby');
} else if (providerName === 'goerli') {
name = this.context.t('goerli');
} else if (providerName === 'fantom') {
name = this.context.t('fantom');
} else {
name = provider.nickname || this.context.t('unknownNetwork');
}
Expand Down Expand Up @@ -285,6 +287,7 @@ class NetworkDropdown extends Component {
{this.renderNetworkEntry('kovan')}
{this.renderNetworkEntry('rinkeby')}
{this.renderNetworkEntry('goerli')}
{this.renderNetworkEntry('fantom')}

{this.renderCustomRpcList(rpcListDetail, this.props.provider)}
<DropdownMenuItem
Expand Down
12 changes: 9 additions & 3 deletions ui/app/components/app/dropdowns/tests/network-dropdown.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ describe('Network Dropdown', function () {
wrapper = mountWithRouter(<NetworkDropdown store={store} />);
});

it('renders 8 DropDownMenuItems ', function () {
assert.strictEqual(wrapper.find(DropdownMenuItem).length, 8);
it('renders 9 DropDownMenuItems ', function () {
assert.strictEqual(wrapper.find(DropdownMenuItem).length, 9);
});

it('checks background color for first ColorIndicator', function () {
Expand Down Expand Up @@ -97,14 +97,20 @@ describe('Network Dropdown', function () {

it('checks background color for sixth ColorIndicator', function () {
const colorIndicator = wrapper.find(ColorIndicator).at(5);
assert.strictEqual(colorIndicator.prop('color'), 'fantom');
assert.strictEqual(colorIndicator.prop('borderColor'), 'fantom');
});

it('checks background color for seventh ColorIndicator', function () {
const colorIndicator = wrapper.find(ColorIndicator).at(6);
const customNetworkGray = 'ui-2';
assert.strictEqual(colorIndicator.prop('color'), customNetworkGray);
assert.strictEqual(colorIndicator.prop('borderColor'), customNetworkGray);
});

it('checks dropdown for frequestRPCList from state', function () {
assert.strictEqual(
wrapper.find(DropdownMenuItem).at(6).text(),
wrapper.find(DropdownMenuItem).at(7).text(),
'✓http://localhost:7545',
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export default class LoadingNetworkScreen extends PureComponent {
name = this.context.t('connectingToRinkeby');
} else if (providerName === 'goerli') {
name = this.context.t('connectingToGoerli');
} else if (providerName === 'fantom') {
name = this.context.t('connectingToFantom');
} else {
name = this.context.t('connectingTo', [providerId]);
}
Expand Down
2 changes: 2 additions & 0 deletions ui/app/css/design-system/colors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ $ropsten: #ff4a8d;
$kovan: #9064ff;
$rinkeby: #f6c343;
$goerli: #3099f2;
$fantom: #9064ff;

$color-map: (
'ui-1': $ui-1,
Expand Down Expand Up @@ -136,5 +137,6 @@ $color-map: (
'kovan': $kovan,
'rinkeby': $rinkeby,
'goerli': $goerli,
'fantom': $fantom,
'transparent': transparent,
);
4 changes: 4 additions & 0 deletions ui/app/helpers/utils/transactions.util.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ethers } from 'ethers';
import log from 'loglevel';
import { addHexPrefix } from '../../../../app/scripts/lib/util';
import { getEtherscanNetworkPrefix } from '../../../lib/etherscan-prefix-for-network';
import { FANTOM_NETWORK_ID } from '../../../../shared/constants/network';
import {
TRANSACTION_CATEGORIES,
TRANSACTION_GROUP_STATUSES,
Expand Down Expand Up @@ -202,6 +203,9 @@ export function getBlockExplorerUrlForTx(networkId, hash, rpcPrefs = {}) {
if (rpcPrefs.blockExplorerUrl) {
return `${rpcPrefs.blockExplorerUrl.replace(/\/+$/u, '')}/tx/${hash}`;
}
if (networkId === FANTOM_NETWORK_ID) {
return `https://ftmscan.com/tx/${hash}`;
}
const prefix = getEtherscanNetworkPrefix(networkId);
return `https://${prefix}etherscan.io/tx/${hash}`;
}
Expand Down
2 changes: 2 additions & 0 deletions ui/app/pages/routes/routes.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,8 @@ export default class Routes extends Component {
return this.context.t('connectingToRinkeby');
case 'goerli':
return this.context.t('connectingToGoerli');
case 'fantom':
return this.context.t('connectingToFantom');
default:
return this.context.t('connectingTo', [providerId]);
}
Expand Down
11 changes: 11 additions & 0 deletions ui/app/pages/settings/networks-tab/networks-tab.constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
RINKEBY_CHAIN_ID,
ROPSTEN,
ROPSTEN_CHAIN_ID,
FANTOM,
FANTOM_CHAIN_ID,
} from '../../../../../shared/constants/network';

const defaultNetworksData = [
Expand Down Expand Up @@ -57,6 +59,15 @@ const defaultNetworksData = [
ticker: 'ETH',
blockExplorerUrl: 'https://kovan.etherscan.io',
},
{
labelKey: FANTOM,
iconColor: '#9064FF',
providerType: FANTOM,
rpcUrl: `https://rpcapi.fantom.network`,
chainId: FANTOM_CHAIN_ID,
ticker: 'FTM',
blockExplorerUrl: 'https://ftmscan.com',
},
];

export { defaultNetworksData };
2 changes: 2 additions & 0 deletions ui/lib/account-link.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export default function getAccountLink(address, network, rpcPrefs) {
return `https://kovan.etherscan.io/address/${address}`;
case 5: // goerli test net
return `https://goerli.etherscan.io/address/${address}`;
case 250: // fantom net
return `https://ftmscan.com/address/${address}`;
default:
return '';
}
Expand Down