project init
This commit is contained in:
commit
715ef231f5
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
build
|
||||
dist
|
||||
.DS_Store
|
||||
yarn-error.log
|
26
package.json
Normal file
26
package.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "jcwallet",
|
||||
"version": "1.0.0",
|
||||
"description": "embed wallet for game",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "rm -rf ./dist && webpack && tsc --declaration -p ./ -t es2015 --emitDeclarationOnly --outDir dist ",
|
||||
"dts": "tsc --declaration -p ./ -t es2015 --emitDeclarationOnly --outDir dist "
|
||||
},
|
||||
"author": "zhl",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@metamask/eth-sig-util": "^4.0.1",
|
||||
"bip39": "^3.0.4",
|
||||
"ethereumjs-wallet": "^1.0.2",
|
||||
"web3": "^1.7.4",
|
||||
"whatwg-fetch": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ts-loader": "~8.2.0",
|
||||
"typescript": "~4.1.5",
|
||||
"webpack": "^4.44.2",
|
||||
"webpack-cli": "^4.9.1"
|
||||
}
|
||||
}
|
326
src/abis/abiERC1155.ts
Normal file
326
src/abis/abiERC1155.ts
Normal file
@ -0,0 +1,326 @@
|
||||
import { AbiItem } from "web3-utils"
|
||||
export const abiERC1155: AbiItem[] = [
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: 'string',
|
||||
name: 'uri_',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'constructor',
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
internalType: 'address',
|
||||
name: 'account',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
internalType: 'address',
|
||||
name: 'operator',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
internalType: 'bool',
|
||||
name: 'approved',
|
||||
type: 'bool',
|
||||
},
|
||||
],
|
||||
name: 'ApprovalForAll',
|
||||
type: 'event',
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
internalType: 'address',
|
||||
name: 'operator',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
internalType: 'address',
|
||||
name: 'from',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
internalType: 'address',
|
||||
name: 'to',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
internalType: 'uint256[]',
|
||||
name: 'ids',
|
||||
type: 'uint256[]',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
internalType: 'uint256[]',
|
||||
name: 'values',
|
||||
type: 'uint256[]',
|
||||
},
|
||||
],
|
||||
name: 'TransferBatch',
|
||||
type: 'event',
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
internalType: 'address',
|
||||
name: 'operator',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
internalType: 'address',
|
||||
name: 'from',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
internalType: 'address',
|
||||
name: 'to',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
internalType: 'uint256',
|
||||
name: 'id',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
internalType: 'uint256',
|
||||
name: 'value',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'TransferSingle',
|
||||
type: 'event',
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
internalType: 'string',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
internalType: 'uint256',
|
||||
name: 'id',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'URI',
|
||||
type: 'event',
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: 'address',
|
||||
name: 'account',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
internalType: 'uint256',
|
||||
name: 'id',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'balanceOf',
|
||||
outputs: [
|
||||
{
|
||||
internalType: 'uint256',
|
||||
name: '',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: 'address[]',
|
||||
name: 'accounts',
|
||||
type: 'address[]',
|
||||
},
|
||||
{
|
||||
internalType: 'uint256[]',
|
||||
name: 'ids',
|
||||
type: 'uint256[]',
|
||||
},
|
||||
],
|
||||
name: 'balanceOfBatch',
|
||||
outputs: [
|
||||
{
|
||||
internalType: 'uint256[]',
|
||||
name: '',
|
||||
type: 'uint256[]',
|
||||
},
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: 'address',
|
||||
name: 'account',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
internalType: 'address',
|
||||
name: 'operator',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
name: 'isApprovedForAll',
|
||||
outputs: [
|
||||
{
|
||||
internalType: 'bool',
|
||||
name: '',
|
||||
type: 'bool',
|
||||
},
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: 'address',
|
||||
name: 'from',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
internalType: 'address',
|
||||
name: 'to',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
internalType: 'uint256[]',
|
||||
name: 'ids',
|
||||
type: 'uint256[]',
|
||||
},
|
||||
{
|
||||
internalType: 'uint256[]',
|
||||
name: 'amounts',
|
||||
type: 'uint256[]',
|
||||
},
|
||||
{
|
||||
internalType: 'bytes',
|
||||
name: 'data',
|
||||
type: 'bytes',
|
||||
},
|
||||
],
|
||||
name: 'safeBatchTransferFrom',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: 'address',
|
||||
name: 'from',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
internalType: 'address',
|
||||
name: 'to',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
internalType: 'uint256',
|
||||
name: 'id',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
internalType: 'uint256',
|
||||
name: 'amount',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
internalType: 'bytes',
|
||||
name: 'data',
|
||||
type: 'bytes',
|
||||
},
|
||||
],
|
||||
name: 'safeTransferFrom',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: 'address',
|
||||
name: 'operator',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
internalType: 'bool',
|
||||
name: 'approved',
|
||||
type: 'bool',
|
||||
},
|
||||
],
|
||||
name: 'setApprovalForAll',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: 'bytes4',
|
||||
name: 'interfaceId',
|
||||
type: 'bytes4',
|
||||
},
|
||||
],
|
||||
name: 'supportsInterface',
|
||||
outputs: [
|
||||
{
|
||||
internalType: 'bool',
|
||||
name: '',
|
||||
type: 'bool',
|
||||
},
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: 'uint256',
|
||||
name: '',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'uri',
|
||||
outputs: [
|
||||
{
|
||||
internalType: 'string',
|
||||
name: '',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
]
|
274
src/abis/abiERC20.ts
Normal file
274
src/abis/abiERC20.ts
Normal file
@ -0,0 +1,274 @@
|
||||
import { AbiItem } from "web3-utils"
|
||||
|
||||
export let abiERC20: AbiItem[] = [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'name',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_spender',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_value',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'approve',
|
||||
outputs: [
|
||||
{
|
||||
name: 'success',
|
||||
type: 'bool',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'totalSupply',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_from',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_to',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_value',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'transferFrom',
|
||||
outputs: [
|
||||
{
|
||||
name: 'success',
|
||||
type: 'bool',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'decimals',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'version',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_owner',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
name: 'balanceOf',
|
||||
outputs: [
|
||||
{
|
||||
name: 'balance',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'symbol',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_to',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_value',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'transfer',
|
||||
outputs: [
|
||||
{
|
||||
name: 'success',
|
||||
type: 'bool',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_spender',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_value',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
name: '_extraData',
|
||||
type: 'bytes',
|
||||
},
|
||||
],
|
||||
name: 'approveAndCall',
|
||||
outputs: [
|
||||
{
|
||||
name: 'success',
|
||||
type: 'bool',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_owner',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_spender',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
name: 'allowance',
|
||||
outputs: [
|
||||
{
|
||||
name: 'remaining',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
name: '_initialAmount',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
name: '_tokenName',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: '_decimalUnits',
|
||||
type: 'uint8',
|
||||
},
|
||||
{
|
||||
name: '_tokenSymbol',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
type: 'constructor',
|
||||
},
|
||||
{
|
||||
payable: false,
|
||||
type: 'fallback',
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: '_from',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: '_to',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: '_value',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'Transfer',
|
||||
type: 'event',
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: '_owner',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: '_spender',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: '_value',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'Approval',
|
||||
type: 'event',
|
||||
},
|
||||
]
|
378
src/abis/abiERC721.ts
Normal file
378
src/abis/abiERC721.ts
Normal file
@ -0,0 +1,378 @@
|
||||
import { AbiItem } from "web3-utils"
|
||||
|
||||
export const abiERC721: AbiItem[] = [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'interfaceID',
|
||||
type: 'bytes4',
|
||||
},
|
||||
],
|
||||
name: 'supportsInterface',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'name',
|
||||
outputs: [
|
||||
{
|
||||
name: '_name',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_tokenId',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'getApproved',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_approved',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_tokenId',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'approve',
|
||||
outputs: [],
|
||||
payable: true,
|
||||
stateMutability: 'payable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'totalSupply',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_from',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_to',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_tokenId',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'transferFrom',
|
||||
outputs: [],
|
||||
payable: true,
|
||||
stateMutability: 'payable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_owner',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_index',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'tokenOfOwnerByIndex',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_from',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_to',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_tokenId',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'safeTransferFrom',
|
||||
outputs: [],
|
||||
payable: true,
|
||||
stateMutability: 'payable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_index',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'tokenByIndex',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_tokenId',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'ownerOf',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_owner',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
name: 'balanceOf',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'symbol',
|
||||
outputs: [
|
||||
{
|
||||
name: '_symbol',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_operator',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_approved',
|
||||
type: 'bool',
|
||||
},
|
||||
],
|
||||
name: 'setApprovalForAll',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_from',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_to',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_tokenId',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
name: 'data',
|
||||
type: 'bytes',
|
||||
},
|
||||
],
|
||||
name: 'safeTransferFrom',
|
||||
outputs: [],
|
||||
payable: true,
|
||||
stateMutability: 'payable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_tokenId',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'tokenURI',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_owner',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: '_operator',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
name: 'isApprovedForAll',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: '_from',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: '_to',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: '_tokenId',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'Transfer',
|
||||
type: 'event',
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: '_owner',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: '_approved',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: '_tokenId',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'Approval',
|
||||
type: 'event',
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: '_owner',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: '_operator',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: '_approved',
|
||||
type: 'bool',
|
||||
},
|
||||
],
|
||||
name: 'ApprovalForAll',
|
||||
type: 'event',
|
||||
},
|
||||
]
|
35
src/common/WalletEvent.ts
Normal file
35
src/common/WalletEvent.ts
Normal file
@ -0,0 +1,35 @@
|
||||
let createWalletEvents = () => ({
|
||||
events: {},
|
||||
emit (event, ...args) {
|
||||
for (let i of this.events[event] || []) {
|
||||
i(...args)
|
||||
}
|
||||
},
|
||||
on (event, cb) {
|
||||
;(this.events[event] = this.events[event] || []).push(cb)
|
||||
return () => (this.events[event] = this.events[event].filter(i => i !== cb))
|
||||
},
|
||||
listen(event, cb) {
|
||||
;(this.events[event] = this.events[event] || []).push(cb)
|
||||
return () => (this.events[event] = this.events[event].filter(i => i !== cb))
|
||||
}
|
||||
})
|
||||
|
||||
export { createWalletEvents }
|
||||
|
||||
export const WALLET_CHAIN_CHANGE = 'wallet_chain_change'
|
||||
|
||||
export const WALLET_ACCOUNT_CHANGE = 'wallet_account_change'
|
||||
|
||||
export const WALLET_TOKEN_TYPE_CHANGE = 'wallet_token_type_change'
|
||||
|
||||
// BEGIN of UI
|
||||
export const WALLET_SHOW_QR = 'wallet_show_qr'
|
||||
|
||||
export const WALLET_SHOW_ACCOUNT_LIST = 'wallet_show_account_list'
|
||||
|
||||
export const WALLET_HIDE_ACCOUNT_LIST = 'wallet_hide_account_list'
|
||||
|
||||
export const WALLET_SHOW_MODAL = 'wallet_show_modal'
|
||||
|
||||
export const WALLET_HIDE_MODAL = 'wallet_hide_modal'
|
12
src/common/ZError.ts
Normal file
12
src/common/ZError.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export class ZError implements Error {
|
||||
code: string;
|
||||
statusCode?: number;
|
||||
message: string;
|
||||
name: string;
|
||||
|
||||
|
||||
constructor(statusCode: number, message: string) {
|
||||
this.statusCode = statusCode;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
71
src/config/chain_config.ts
Normal file
71
src/config/chain_config.ts
Normal file
@ -0,0 +1,71 @@
|
||||
export const DEFALUT_TOKENS = {
|
||||
321: [
|
||||
{
|
||||
type: 'eth',
|
||||
address: 'eth',
|
||||
symbol: 'KCS',
|
||||
decimal: 18
|
||||
},
|
||||
{
|
||||
type: 'erc20',
|
||||
address: '0xcaA011E902103752435AC98657B046B282E36509',
|
||||
symbol: 'CEC',
|
||||
decimal: 18
|
||||
},
|
||||
{
|
||||
type: 'erc20',
|
||||
address: '0xBE023C9D294A4668f0d510E2570CB81B4536cE77',
|
||||
symbol: 'CEG',
|
||||
decimal: 18
|
||||
}
|
||||
],
|
||||
322: [
|
||||
{
|
||||
type: 'eth',
|
||||
address: 'eth',
|
||||
symbol: 'tKCS',
|
||||
decimal: 18
|
||||
},
|
||||
{
|
||||
type: 'erc20',
|
||||
address: '0xdb6D4bB22E2C12686Efff25a79EC78f9f078fe7D',
|
||||
symbol: 'CEC',
|
||||
decimal: 18
|
||||
},
|
||||
{
|
||||
type: 'erc20',
|
||||
address: '0xC5Cd606b3e9B80b758e8274B198c76D929aA094A',
|
||||
symbol: 'CEG',
|
||||
decimal: 18
|
||||
}
|
||||
],
|
||||
97: [
|
||||
{
|
||||
type: 'eth',
|
||||
address: 'eth',
|
||||
symbol: 'BNB',
|
||||
decimal: 18
|
||||
}
|
||||
],
|
||||
56: [
|
||||
{
|
||||
type: 'eth',
|
||||
address: 'eth',
|
||||
symbol: 'BNB',
|
||||
decimal: 18
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const DEFAULT_NFT_TYPES = {
|
||||
321: {
|
||||
hero: '0x0EB362BD40F2288fF25A6Ee1b487cB0cb4638e0D',
|
||||
weapon: '0x29F67A372AC1c6AcF478A564992D421FE20F2cc8',
|
||||
chip: '0x54B6ED7EDe9355b471985439421Aa1DC7Da6Dc20'
|
||||
},
|
||||
322: {
|
||||
hero: '0x52917087cd4E48bDb5f336012E677f471f9E1C2D',
|
||||
weapon: '0x500AD8A4D50d71Af5cA8eA3b12B914f7aE5466f7',
|
||||
chip: '0x0640958BDb4D7956e1452FacEBD550C6Cf42aC94'
|
||||
}
|
||||
}
|
1
src/config/constants.ts
Normal file
1
src/config/constants.ts
Normal file
@ -0,0 +1 @@
|
||||
export const WALLET_STORAGE_KEY_NAME = 'jc_wallet_data'
|
62
src/data/DataModel.ts
Normal file
62
src/data/DataModel.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { DEFALUT_TOKENS } from "../config/chain_config"
|
||||
|
||||
export interface IToken {
|
||||
address: string
|
||||
type: 'eth'|'erc20'
|
||||
default: boolean
|
||||
symbol?: string
|
||||
balance?: string
|
||||
decimal: number
|
||||
image?: string
|
||||
last?: number
|
||||
}
|
||||
|
||||
export interface INFT {
|
||||
address: string
|
||||
index: number
|
||||
tokenId?: string
|
||||
image?: string
|
||||
name?: string
|
||||
desc?: string
|
||||
last?: number
|
||||
}
|
||||
|
||||
export function initNFT(address: string, index: number) {
|
||||
return {
|
||||
address,
|
||||
index
|
||||
}
|
||||
}
|
||||
|
||||
export interface ITokenData {
|
||||
tokens: IToken[]
|
||||
heros: INFT[]
|
||||
weapons: INFT[]
|
||||
chips: INFT[]
|
||||
}
|
||||
export interface IAccount {
|
||||
address: string
|
||||
nickname?: string
|
||||
avatar?: string
|
||||
tokenData: {
|
||||
[key: number]: ITokenData
|
||||
}
|
||||
}
|
||||
|
||||
export function initAccount(address: string, chain: number, nickname: string): IAccount {
|
||||
let chainData = {}
|
||||
let data: IAccount = {
|
||||
address,
|
||||
nickname,
|
||||
tokenData: chainData
|
||||
}
|
||||
let tokens = DEFALUT_TOKENS[chain]
|
||||
chainData[chain] = {
|
||||
tokens,
|
||||
heros: [],
|
||||
weapons: [],
|
||||
chips: []
|
||||
}
|
||||
//TODO: add default tokens
|
||||
return data
|
||||
}
|
278
src/data/allchain.ts
Normal file
278
src/data/allchain.ts
Normal file
@ -0,0 +1,278 @@
|
||||
export const AllChains = [
|
||||
{
|
||||
name: 'Ethereum Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://rpc.ankr.com/eth',
|
||||
id: 1,
|
||||
symbol: 'ETH',
|
||||
explorerurl: 'https://etherscan.io'
|
||||
},
|
||||
{
|
||||
name: 'Ethereum Ropsten Testnet RPC',
|
||||
type: 'Testnet',
|
||||
rpc: 'https://ropsten.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161',
|
||||
id: 3,
|
||||
symbol: 'ETH',
|
||||
explorerurl: 'https://ropsten.etherscan.io'
|
||||
},
|
||||
{
|
||||
name: 'Ethereum Rinkeby Testnet RPC',
|
||||
type: 'Testnet',
|
||||
rpc: 'https://rinkey.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161',
|
||||
id: 4,
|
||||
symbol: 'ETH',
|
||||
explorerurl: 'https://rinkey.etherscan.io'
|
||||
},
|
||||
{
|
||||
name: 'Ethereum Goerli Testnet RPC',
|
||||
type: 'Testnet',
|
||||
rpc: 'https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161',
|
||||
id: 5,
|
||||
symbol: 'ETH',
|
||||
explorerurl: 'https://goerli.etherscan.io'
|
||||
},
|
||||
{
|
||||
name: 'Ethereum Kovan Testnet RPC',
|
||||
type: 'Testnet',
|
||||
rpc: 'https://kovan.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161',
|
||||
id: 6,
|
||||
symbol: 'ETH',
|
||||
explorerurl: 'https://kovan.etherscan.io'
|
||||
},
|
||||
{
|
||||
name: 'Ubiq Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://rpc.octano.dev/',
|
||||
id: 8,
|
||||
symbol: 'UBQ',
|
||||
explorerurl: 'https://ubiqscan.io/'
|
||||
},
|
||||
{
|
||||
name: 'Elastos ETH Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://api.elastos.io/eth',
|
||||
id: 20,
|
||||
symbol: 'ELA',
|
||||
explorerurl: 'https://explorer.elaeth.io/'
|
||||
},
|
||||
{
|
||||
name: 'Cronos Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://evm-cronos.crypto.org',
|
||||
id: 25,
|
||||
symbol: 'CRO',
|
||||
explorerurl: 'https://cronos.crypto.org/explorer/'
|
||||
},
|
||||
{
|
||||
name: 'Telos EVM Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://mainnet.telos.net/evm',
|
||||
id: 40,
|
||||
symbol: 'TLOS',
|
||||
explorerurl: 'https://telos.net/'
|
||||
},
|
||||
{
|
||||
name: 'Binance Smart Chain',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://rpc.ankr.com/bsc',
|
||||
logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTAwIiBoZWlnaHQ9IjI1MDAiIHZpZXdCb3g9IjAgMCAyMDAwIDIwMDAiPjxnIGZpbGw9IiNmM2JhMmYiPjxwYXRoIGQ9Ik02MTEuNTkgODQwLjQybDM4OC40LTM4OC4zOSAzODguNiAzODguNTkgMjI2LTIyNkw5OTkuOTkgMCAzODUuNiA2MTQuNDJsMjI1Ljk5IDIyNk0uMDA2IDk5OS45NjlsMjI2LjAwNy0yMjYuMDA3IDIyNS45OTIgMjI1Ljk5M0wyMjYgMTIyNS45NnpNNjExLjU5IDExNTkuNThsMzg4LjQgMzg4LjM5IDM4OC41OS0zODguNTggMjI2LjEyIDIyNS44OC0uMTEuMTJMOTk5Ljk5IDIwMDBsLTYxNC40MS02MTQuNC0uMzItLjMyIDIyNi4zMy0yMjUuN00xNTQ4LjAxMyAxMDAwLjA5M2wyMjYuMDA3LTIyNi4wMDYgMjI1Ljk5MiAyMjUuOTkyLTIyNi4wMDYgMjI2LjAwN3oiLz48cGF0aCBkPSJNMTIyOS4yMiA5OTkuODhoLjFMOTk5Ljk5IDc3MC41NSA4MzAuNTEgOTQwLjAzaC0uMDFsLTE5LjQ3IDE5LjQ4LTQwLjE2IDQwLjE3LS4zMi4zMS4zMi4zMyAyMjkuMTIgMjI5LjEzIDIyOS4zMy0yMjkuMzMuMTEtLjEzLS4yMS0uMTEiLz48L2c+PC9zdmc+',
|
||||
id: 56,
|
||||
symbol: 'BNB',
|
||||
explorerurl: 'https://bscscan.com'
|
||||
},
|
||||
{
|
||||
name: 'OKExChain Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://exchainrpc.okex.org',
|
||||
id: 66,
|
||||
symbol: 'OKT',
|
||||
explorerurl: 'https://www.oklink.com/okexchain'
|
||||
},
|
||||
{
|
||||
name: 'Hoo Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://http-mainnet.hoosmartchain.com',
|
||||
id: 70,
|
||||
symbol: 'HOO',
|
||||
explorerurl: 'https://hooscan.com'
|
||||
},
|
||||
{
|
||||
name: 'Binance Testnet',
|
||||
type: 'Testnet',
|
||||
rpc: 'https://data-seed-prebsc-1-s1.binance.org:8545/',
|
||||
id: 97,
|
||||
logo: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTAwIiBoZWlnaHQ9IjI1MDAiIHZpZXdCb3g9IjAgMCAyMDAwIDIwMDAiPjxnIGZpbGw9IiNmM2JhMmYiPjxwYXRoIGQ9Ik02MTEuNTkgODQwLjQybDM4OC40LTM4OC4zOSAzODguNiAzODguNTkgMjI2LTIyNkw5OTkuOTkgMCAzODUuNiA2MTQuNDJsMjI1Ljk5IDIyNk0uMDA2IDk5OS45NjlsMjI2LjAwNy0yMjYuMDA3IDIyNS45OTIgMjI1Ljk5M0wyMjYgMTIyNS45NnpNNjExLjU5IDExNTkuNThsMzg4LjQgMzg4LjM5IDM4OC41OS0zODguNTggMjI2LjEyIDIyNS44OC0uMTEuMTJMOTk5Ljk5IDIwMDBsLTYxNC40MS02MTQuNC0uMzItLjMyIDIyNi4zMy0yMjUuN00xNTQ4LjAxMyAxMDAwLjA5M2wyMjYuMDA3LTIyNi4wMDYgMjI1Ljk5MiAyMjUuOTkyLTIyNi4wMDYgMjI2LjAwN3oiLz48cGF0aCBkPSJNMTIyOS4yMiA5OTkuODhoLjFMOTk5Ljk5IDc3MC41NSA4MzAuNTEgOTQwLjAzaC0uMDFsLTE5LjQ3IDE5LjQ4LTQwLjE2IDQwLjE3LS4zMi4zMS4zMi4zMyAyMjkuMTIgMjI5LjEzIDIyOS4zMy0yMjkuMzMuMTEtLjEzLS4yMS0uMTEiLz48L2c+PC9zdmc+',
|
||||
symbol: 'BNB',
|
||||
explorerurl: 'https://testnet.bscscan.com'
|
||||
},
|
||||
{
|
||||
name: 'xDai Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://rpc.xdaichain.com/',
|
||||
id: 100,
|
||||
symbol: 'XDAI',
|
||||
explorerurl: 'https://blockscout.com/xdai/mainnet/'
|
||||
},
|
||||
{
|
||||
name: 'Fuse Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://rpc.fuse.io',
|
||||
id: 122,
|
||||
symbol: 'FUSE',
|
||||
explorerurl: 'https://explorer.fuse.io/'
|
||||
},
|
||||
{
|
||||
name: 'HECO Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://http-mainnet-node.huobichain.com/',
|
||||
id: 128,
|
||||
symbol: 'HT',
|
||||
explorerurl: 'https://hecoinfo.com/'
|
||||
},
|
||||
{
|
||||
name: 'Matic Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://polygon-rpc.com',
|
||||
id: 137,
|
||||
symbol: 'MATIC',
|
||||
explorerurl: 'https://explorer.matic.network/'
|
||||
},
|
||||
{
|
||||
name: 'Fantom Opera Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://rpc.ftm.tools/',
|
||||
id: 250,
|
||||
symbol: 'FTM',
|
||||
explorerurl: 'https://ftmscan.com'
|
||||
},
|
||||
{
|
||||
name: 'HECO Testnet RPC',
|
||||
type: 'Testnet',
|
||||
rpc: 'https://http-testnet.hecochain.com',
|
||||
id: 256,
|
||||
symbol: 'HT',
|
||||
explorerurl: 'https://testnet.hecoinfo.com/'
|
||||
},
|
||||
{
|
||||
name: 'KCC Mainnet',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://rpc-mainnet.kcc.network',
|
||||
id: 321,
|
||||
logo: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAzMiAzMiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzIgMzI7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojMDA5M0REO30KCS5zdDF7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO30KPC9zdHlsZT4KPGc+Cgk8Y2lyY2xlIGNsYXNzPSJzdDAiIGN4PSIxNiIgY3k9IjE2IiByPSIxNiIvPgoJPHBhdGggY2xhc3M9InN0MSIgZD0iTTEzLjUsMTZsNS4yLDUuM0wyMiwxOGMwLjYtMC42LDEuNS0wLjYsMiwwYzAsMCwwLDAsMCwwYzAuNiwwLjYsMC42LDEuNiwwLDIuMmwtNC4zLDQuNAoJCWMtMC42LDAuNi0xLjUsMC42LTIuMSwwYzAsMCwwLDAsMCwwbC02LjItNi40VjIyYzAsMC44LTAuNywxLjUtMS41LDEuNWMtMC44LDAtMS41LTAuNy0xLjUtMS41VjEwYzAtMC44LDAuNy0xLjUsMS41LTEuNQoJCWMwLjgsMCwxLjUsMC43LDEuNSwxLjV2My44bDYuMi02LjRjMC42LTAuNiwxLjUtMC42LDIuMSwwYzAsMCwwLDAsMCwwbDQuMyw0LjRjMC42LDAuNiwwLjYsMS42LDAsMi4yYy0wLjYsMC42LTEuNSwwLjYtMiwwCgkJYzAsMCwwLDAsMCwwbC0zLjMtMy40TDEzLjUsMTZ6IE0xOC43LDE0LjVjMC44LDAsMS41LDAuNywxLjUsMS41cy0wLjcsMS41LTEuNSwxLjVzLTEuNS0wLjctMS41LTEuNQoJCUMxNy4yLDE1LjIsMTcuOSwxNC41LDE4LjcsMTQuNXoiLz4KPC9nPgo8L3N2Zz4K',
|
||||
symbol: 'KCS',
|
||||
explorerurl: 'https://scan.kcc.network'
|
||||
},
|
||||
{
|
||||
name: 'KCC Testnet',
|
||||
type: 'Testnet',
|
||||
logo: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAzMiAzMiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzIgMzI7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojMDA5M0REO30KCS5zdDF7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO30KPC9zdHlsZT4KPGc+Cgk8Y2lyY2xlIGNsYXNzPSJzdDAiIGN4PSIxNiIgY3k9IjE2IiByPSIxNiIvPgoJPHBhdGggY2xhc3M9InN0MSIgZD0iTTEzLjUsMTZsNS4yLDUuM0wyMiwxOGMwLjYtMC42LDEuNS0wLjYsMiwwYzAsMCwwLDAsMCwwYzAuNiwwLjYsMC42LDEuNiwwLDIuMmwtNC4zLDQuNAoJCWMtMC42LDAuNi0xLjUsMC42LTIuMSwwYzAsMCwwLDAsMCwwbC02LjItNi40VjIyYzAsMC44LTAuNywxLjUtMS41LDEuNWMtMC44LDAtMS41LTAuNy0xLjUtMS41VjEwYzAtMC44LDAuNy0xLjUsMS41LTEuNQoJCWMwLjgsMCwxLjUsMC43LDEuNSwxLjV2My44bDYuMi02LjRjMC42LTAuNiwxLjUtMC42LDIuMSwwYzAsMCwwLDAsMCwwbDQuMyw0LjRjMC42LDAuNiwwLjYsMS42LDAsMi4yYy0wLjYsMC42LTEuNSwwLjYtMiwwCgkJYzAsMCwwLDAsMCwwbC0zLjMtMy40TDEzLjUsMTZ6IE0xOC43LDE0LjVjMC44LDAsMS41LDAuNywxLjUsMS41cy0wLjcsMS41LTEuNSwxLjVzLTEuNS0wLjctMS41LTEuNQoJCUMxNy4yLDE1LjIsMTcuOSwxNC41LDE4LjcsMTQuNXoiLz4KPC9nPgo8L3N2Zz4K',
|
||||
rpc: 'https://rpc-testnet.kcc.network',
|
||||
id: 322,
|
||||
symbol: 'tKCS',
|
||||
explorerurl: 'https://scan-testnet.kcc.network'
|
||||
},
|
||||
{
|
||||
name: 'Moonriver Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://rpc.moonriver.moonbeam.network',
|
||||
id: 1285,
|
||||
symbol: 'MOVR',
|
||||
explorerurl: 'https://blockscout.moonriver.moonbeam.network/'
|
||||
},
|
||||
{
|
||||
name: 'Fantom Testnet RPC',
|
||||
type: 'Testnet',
|
||||
rpc: 'https://rpc.testnet.fantom.network/',
|
||||
id: 4002,
|
||||
symbol: 'FTM',
|
||||
explorerurl: 'https://testnet.ftmscan.com'
|
||||
},
|
||||
{
|
||||
name: 'IoTeX Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://babel-api.mainnet.iotex.io',
|
||||
id: 4689,
|
||||
symbol: 'IOTEX',
|
||||
explorerurl: 'https://iotexscan.io/'
|
||||
},
|
||||
{
|
||||
name: 'Nahmii Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://l2.nahmii.io/',
|
||||
id: 5551,
|
||||
symbol: 'ETH',
|
||||
explorerurl: 'https://explorer.nahmii.io/'
|
||||
},
|
||||
{
|
||||
name: 'Nahmii Testnet RPC',
|
||||
type: 'Testnet',
|
||||
rpc: 'https://l2.testnet.nahmii.io/',
|
||||
id: 5553,
|
||||
symbol: 'ETH',
|
||||
explorerurl: 'https://explorer.testnet.nahmii.io/'
|
||||
},
|
||||
{
|
||||
name: 'Arbitrum Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://rpc.ankr.com/arbitrum',
|
||||
id: 42161,
|
||||
symbol: 'ETH',
|
||||
explorerurl: 'https://arbiscan.io/'
|
||||
},
|
||||
{
|
||||
name: 'Celo Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://rpc.ankr.com/celo',
|
||||
id: 42220,
|
||||
symbol: 'CELO',
|
||||
explorerurl: 'https://celoscan.com'
|
||||
},
|
||||
{
|
||||
name: 'Avalanche C Chain Local RPC',
|
||||
type: 'Testnet',
|
||||
rpc: 'https://localhost:9650/ext/bc/C/rpc',
|
||||
id: 43112,
|
||||
symbol: 'AVAX',
|
||||
explorerurl: 'https://snowtrace.io'
|
||||
},
|
||||
{
|
||||
name: 'Avalanche FUJI Testnet RPC',
|
||||
type: 'Testnet',
|
||||
rpc: 'https://api.avax-test.network/ext/bc/C/rpc',
|
||||
id: 43113,
|
||||
symbol: 'AVAX',
|
||||
explorerurl: 'https://testnet.explorer.avax.network/'
|
||||
},
|
||||
{
|
||||
name: 'Avalanche C Chain Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://rpc.ankr.com/avalanche',
|
||||
id: 43114,
|
||||
symbol: 'AVAX',
|
||||
explorerurl: 'https://snowtrace.io'
|
||||
},
|
||||
{
|
||||
name: 'Matic Testnet RPC',
|
||||
type: 'Testnet',
|
||||
rpc: 'https://rpc-mumbai.maticvigil.com',
|
||||
id: 80001,
|
||||
symbol: 'MATIC',
|
||||
explorerurl: 'https://mumbai.polygonscan.com/'
|
||||
},
|
||||
{
|
||||
name: 'Harmony Mainnet RPC',
|
||||
type: 'Mainnet',
|
||||
rpc: 'https://api.harmony.one/',
|
||||
id: 1666600000,
|
||||
symbol: 'ONE',
|
||||
explorerurl: 'https://explorer.harmony.one'
|
||||
},
|
||||
{
|
||||
name: 'Harmony Testnet RPC',
|
||||
type: 'Testnet',
|
||||
rpc: 'https://api.s0.b.hmny.io/',
|
||||
id: 1666700000,
|
||||
symbol: 'ONE',
|
||||
explorerurl: 'https://explorer.harmony.one'
|
||||
}
|
||||
]
|
27
src/decorator/singleton.decorator.ts
Normal file
27
src/decorator/singleton.decorator.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @singleton
|
||||
* class Test {}
|
||||
* new Test() === new Test() // returns `true`
|
||||
* const TestSingleton = singleton(Test)
|
||||
* new TestSingleton() === new TestSingleton() //returns 'true'
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line symbol-description
|
||||
export const SINGLETON_KEY = Symbol()
|
||||
|
||||
export type Singleton<T extends new (...args: any[]) => any> = T & {
|
||||
[SINGLETON_KEY]: T extends new (...args: any[]) => infer I ? I : never
|
||||
}
|
||||
export const singleton = <T extends new (...args: any[]) => any>(classTarget: T) =>
|
||||
new Proxy(classTarget, {
|
||||
construct(target: Singleton<T>, argumentsList, newTarget) {
|
||||
// Skip proxy for children
|
||||
if (target.prototype !== newTarget.prototype) {
|
||||
return Reflect.construct(target, argumentsList, newTarget)
|
||||
}
|
||||
if (!target[SINGLETON_KEY]) {
|
||||
target[SINGLETON_KEY] = Reflect.construct(target, argumentsList, newTarget)
|
||||
}
|
||||
return target[SINGLETON_KEY]
|
||||
}
|
||||
})
|
235
src/index.ts
Normal file
235
src/index.ts
Normal file
@ -0,0 +1,235 @@
|
||||
|
||||
import { singleton } from "./decorator/singleton.decorator";
|
||||
import Web3 from 'web3';
|
||||
import 'whatwg-fetch'
|
||||
import { ZError } from "./common/ZError";
|
||||
import { AllChains } from "./data/allchain";
|
||||
import { createWalletEvents, WALLET_ACCOUNT_CHANGE, WALLET_CHAIN_CHANGE, WALLET_TOKEN_TYPE_CHANGE } from "./common/WalletEvent";
|
||||
import { ERC20Standard } from "./standards/ERC20Standard";
|
||||
import { ERC721Standard } from "./standards/ERC721Standard";
|
||||
import { IAccount, initAccount } from "./data/DataModel";
|
||||
import { DataManage } from "./manage/DataManage";
|
||||
import { WALLET_STORAGE_KEY_NAME } from "./config/constants";
|
||||
import { DEFALUT_TOKENS } from "./config/chain_config";
|
||||
import { recoverTypedSignature, signTypedData, SignTypedDataVersion } from "@metamask/eth-sig-util";
|
||||
|
||||
var global =
|
||||
(typeof globalThis !== 'undefined' && globalThis) ||
|
||||
(typeof self !== 'undefined' && self) ||
|
||||
(typeof global !== 'undefined' && global) ||
|
||||
{}
|
||||
declare global {
|
||||
interface Window {
|
||||
jc: {
|
||||
wallet: JCWallet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface IChainData {
|
||||
name: string,
|
||||
type: string,
|
||||
rpc: string,
|
||||
id: number,
|
||||
symbol: string,
|
||||
explorerurl: string
|
||||
}
|
||||
@singleton
|
||||
export default class JCWallet {
|
||||
web3: Web3 = null
|
||||
private wallet: any = null
|
||||
private password: string = '111111'
|
||||
private chainSet: Set<number> = new Set()
|
||||
private chainMap: Map<number, IChainData> = new Map()
|
||||
private _currentChain: IChainData
|
||||
public erc20Standard: ERC20Standard
|
||||
public erc721Standard: ERC721Standard
|
||||
public mainHandlers = createWalletEvents()
|
||||
public uiHandlers = createWalletEvents()
|
||||
private dataManage = new DataManage()
|
||||
public data: IAccount[] = []
|
||||
public iconType = 'jazz'
|
||||
private accountIndex = 0
|
||||
|
||||
constructor() {
|
||||
// this.web3 = new Web3('https://rpc-mainnet.kcc.network')
|
||||
this.web3 = new Web3('https://rpc-testnet.kcc.network')
|
||||
this.erc20Standard = new ERC20Standard(this.web3);
|
||||
this.erc721Standard = new ERC721Standard(this.web3);
|
||||
this.wallet = this.web3.eth.accounts.wallet.load(this.password, WALLET_STORAGE_KEY_NAME)
|
||||
if (!this.wallet || this.wallet.length === 0) {
|
||||
let key = '0xa6c4354fb93a55fb67117969a12465209395ec31089fea9e6e061f873b87a473'
|
||||
this.wallet.add(key);
|
||||
this.web3.eth.accounts.wallet.save(this.password, WALLET_STORAGE_KEY_NAME);
|
||||
}
|
||||
this.data = this.dataManage.loadData()
|
||||
window.jc = { wallet: this };
|
||||
}
|
||||
|
||||
|
||||
public init({chains}: {chains: number[]}) {
|
||||
for (let chain of chains) {
|
||||
this.chainSet.add(chain)
|
||||
if (!this.chainMap.has(chain)) {
|
||||
let data = AllChains.find(o => o.id === chain)
|
||||
if (data) {
|
||||
this.chainMap.set(chain, data);
|
||||
if (!this._currentChain) {
|
||||
this._currentChain = data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get currentChain() {
|
||||
return this._currentChain
|
||||
}
|
||||
|
||||
|
||||
|
||||
updateCurrentChain(chainId: number) {
|
||||
const chainData = this.chainMap.get(chainId)
|
||||
this._currentChain = chainData
|
||||
this.web3.eth.setProvider(chainData.rpc)
|
||||
this.mainHandlers.emit(WALLET_CHAIN_CHANGE, chainData)
|
||||
this.updateListType('tokens')
|
||||
}
|
||||
|
||||
updateListType(type: string) {
|
||||
this.mainHandlers.emit(WALLET_TOKEN_TYPE_CHANGE, type)
|
||||
}
|
||||
|
||||
get chainList() {
|
||||
return [...this.chainMap.values()]
|
||||
}
|
||||
|
||||
public saveLocal() {
|
||||
|
||||
}
|
||||
|
||||
public loadLocal() {
|
||||
|
||||
}
|
||||
|
||||
public saveRemove() {
|
||||
|
||||
}
|
||||
|
||||
public loadRemote() {
|
||||
|
||||
}
|
||||
|
||||
public currentAccount() {
|
||||
return this.wallet[this.accountIndex];
|
||||
}
|
||||
|
||||
get currentAccountData() {
|
||||
let address = this.currentAccount().address;
|
||||
const chain = this.currentChain.id
|
||||
let data = this.data.find(o => o.address === address)
|
||||
if (!data) {
|
||||
let accountName = `Account${this.wallet.length}`
|
||||
data = initAccount(address, chain, accountName)
|
||||
this.data.push(data)
|
||||
} else {
|
||||
if (!data.tokenData[chain]) {
|
||||
let tokens = DEFALUT_TOKENS[chain]
|
||||
data.tokenData[chain] = {
|
||||
tokens,
|
||||
heros: [],
|
||||
weapons: [],
|
||||
chips: []
|
||||
}
|
||||
}
|
||||
}
|
||||
this.dataManage.saveData(this.data)
|
||||
return data
|
||||
}
|
||||
|
||||
get accounts() {
|
||||
return this.data
|
||||
}
|
||||
|
||||
public createAccount() {
|
||||
let account = this.web3.eth.accounts.create()
|
||||
this.wallet.add(account)
|
||||
this.wallet.save(this.password, WALLET_STORAGE_KEY_NAME)
|
||||
this.accountIndex = this.wallet.length - 1
|
||||
this.mainHandlers.emit(WALLET_ACCOUNT_CHANGE, account.address)
|
||||
}
|
||||
|
||||
public selectAccount(address: string) {
|
||||
let index = 0
|
||||
for (let i = 0, l = this.wallet.length; i < l ; i ++) {
|
||||
if (this.wallet[i].address === address) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if (index !== this.accountIndex && index < this.wallet.length) {
|
||||
this.accountIndex = index
|
||||
this.mainHandlers.emit(WALLET_ACCOUNT_CHANGE, this.wallet[index].address)
|
||||
}
|
||||
}
|
||||
|
||||
public importAccount(privateKey: string) {
|
||||
let account = this.web3.eth.accounts.privateKeyToAccount(privateKey)
|
||||
if (this.wallet[account.address]) {
|
||||
return false
|
||||
}
|
||||
this.wallet.add(account);
|
||||
this.web3.eth.accounts.wallet.save(this.password, WALLET_STORAGE_KEY_NAME);
|
||||
this.accountIndex = this.wallet.length - 1
|
||||
this.mainHandlers.emit(WALLET_ACCOUNT_CHANGE, account.address)
|
||||
return true
|
||||
}
|
||||
|
||||
public async sendEth(to: string, amount: number | string) {
|
||||
let from = this.currentAccount().address;
|
||||
const amountToSend = this.web3.utils.toWei(amount+'', "ether");
|
||||
let gas = await this.web3.eth.estimateGas({ from, to, value: amountToSend })
|
||||
this.web3.eth.sendTransaction({ from, to, gas, value: amountToSend });
|
||||
}
|
||||
|
||||
public async getBalance(address?: string) {
|
||||
console.log('get balance with address: ', address);
|
||||
if (!address) {
|
||||
let accountData = this.wallet[this.accountIndex]
|
||||
if (!accountData) {
|
||||
throw new ZError(10, 'no account found')
|
||||
}
|
||||
address = accountData.address
|
||||
}
|
||||
|
||||
let balance = await this.web3.eth.getBalance(address);
|
||||
return balance
|
||||
}
|
||||
|
||||
public signTypedDataV4(signObj: any) {
|
||||
const account = this.currentAccount()
|
||||
return signTypedData({
|
||||
data: signObj,
|
||||
privateKey: Buffer.from(account.privateKey.replace('0x', ''), 'hex'),
|
||||
version: SignTypedDataVersion.V4
|
||||
})
|
||||
}
|
||||
|
||||
public recoverTypedSignatureV4(signObj: any, signature: string) {
|
||||
return recoverTypedSignature({
|
||||
data: signObj,
|
||||
signature,
|
||||
version: SignTypedDataVersion.V4
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// window.jc = window.jc || {wallet: new JCWallet()};
|
||||
|
||||
export * from './common/WalletEvent'
|
||||
export * from './common/ZError'
|
||||
export * from './config/chain_config'
|
||||
export * from './util/number.util'
|
||||
export * from './util/wallet.util'
|
||||
export * from "./data/DataModel";
|
||||
export * from './config/chain_config';
|
9
src/lib/Http.ts
Normal file
9
src/lib/Http.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import 'whatwg-fetch'
|
||||
|
||||
export async function GET(url: string) {
|
||||
return fetch(url)
|
||||
}
|
||||
|
||||
export async function GET_JSON(url: string) {
|
||||
return fetch(url).then(res => {return res.json()})
|
||||
}
|
211
src/lib/mersenne-twister.ts
Normal file
211
src/lib/mersenne-twister.ts
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
https://github.com/banksean wrapped Makoto Matsumoto and Takuji Nishimura's code in a namespace
|
||||
so it's better encapsulated. Now you can have multiple random number generators
|
||||
and they won't stomp all over eachother's state.
|
||||
|
||||
If you want to use this as a substitute for Math.random(), use the random()
|
||||
method like so:
|
||||
|
||||
var m = new MersenneTwister();
|
||||
var randomNumber = m.random();
|
||||
|
||||
You can also call the other genrand_{foo}() methods on the instance.
|
||||
|
||||
If you want to use a specific seed in order to get a repeatable random
|
||||
sequence, pass an integer into the constructor:
|
||||
|
||||
var m = new MersenneTwister(123);
|
||||
|
||||
and that will always produce the same random sequence.
|
||||
|
||||
Sean McCullough (banksean@gmail.com)
|
||||
*/
|
||||
|
||||
/*
|
||||
A C-program for MT19937, with initialization improved 2002/1/26.
|
||||
Coded by Takuji Nishimura and Makoto Matsumoto.
|
||||
|
||||
Before using, initialize the state by using init_seed(seed)
|
||||
or init_by_array(init_key, key_length).
|
||||
|
||||
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of its contributors may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Any feedback is very welcome.
|
||||
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
|
||||
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
|
||||
*/
|
||||
|
||||
export default class MersenneTwister {
|
||||
N = 624;
|
||||
M = 397;
|
||||
MATRIX_A = 0x9908b0df; /* constant vector a */
|
||||
UPPER_MASK = 0x80000000; /* most significant w-r bits */
|
||||
LOWER_MASK = 0x7fffffff; /* least significant r bits */
|
||||
|
||||
mt = new Array(this.N); /* the array for the state vector */
|
||||
mti=this.N+1; /* mti==N+1 means mt[N] is not initialized */
|
||||
|
||||
constructor(seed) {
|
||||
if (seed == undefined) {
|
||||
seed = new Date().getTime();
|
||||
}
|
||||
/* Period parameters */
|
||||
if (seed.constructor == Array) {
|
||||
this.init_by_array(seed, seed.length);
|
||||
}
|
||||
else {
|
||||
this.init_seed(seed);
|
||||
}
|
||||
}
|
||||
|
||||
/* initializes mt[N] with a seed */
|
||||
/* origin name init_genrand */
|
||||
init_seed(s1: number) {
|
||||
this.mt[0] = s1 >>> 0;
|
||||
for (this.mti=1; this.mti<this.N; this.mti++) {
|
||||
var s = this.mt[this.mti-1] ^ (this.mt[this.mti-1] >>> 30);
|
||||
this.mt[this.mti] = (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253)
|
||||
+ this.mti;
|
||||
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
|
||||
/* In the previous versions, MSBs of the seed affect */
|
||||
/* only MSBs of the array mt[]. */
|
||||
/* 2002/01/09 modified by Makoto Matsumoto */
|
||||
this.mt[this.mti] >>>= 0;
|
||||
/* for >32 bit machines */
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize by an array with array-length */
|
||||
/* init_key is the array for initializing keys */
|
||||
/* key_length is its length */
|
||||
/* slight change for C++, 2004/2/26 */
|
||||
init_by_array(init_key, key_length) {
|
||||
var i, j, k;
|
||||
this.init_seed(19650218);
|
||||
i=1; j=0;
|
||||
k = (this.N>key_length ? this.N : key_length);
|
||||
for (; k; k--) {
|
||||
var s = this.mt[i-1] ^ (this.mt[i-1] >>> 30)
|
||||
this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1664525) << 16) + ((s & 0x0000ffff) * 1664525)))
|
||||
+ init_key[j] + j; /* non linear */
|
||||
this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */
|
||||
i++; j++;
|
||||
if (i>=this.N) { this.mt[0] = this.mt[this.N-1]; i=1; }
|
||||
if (j>=key_length) j=0;
|
||||
}
|
||||
for (k=this.N-1; k; k--) {
|
||||
var s = this.mt[i-1] ^ (this.mt[i-1] >>> 30);
|
||||
this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1566083941) << 16) + (s & 0x0000ffff) * 1566083941))
|
||||
- i; /* non linear */
|
||||
this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */
|
||||
i++;
|
||||
if (i>=this.N) { this.mt[0] = this.mt[this.N-1]; i=1; }
|
||||
}
|
||||
|
||||
this.mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */
|
||||
}
|
||||
|
||||
/* generates a random number on [0,0xffffffff]-interval */
|
||||
/* origin name genrand_int32 */
|
||||
random_int() {
|
||||
var y;
|
||||
var mag01 = new Array(0x0, this.MATRIX_A);
|
||||
/* mag01[x] = x * MATRIX_A for x=0,1 */
|
||||
|
||||
if (this.mti >= this.N) { /* generate N words at one time */
|
||||
var kk;
|
||||
|
||||
if (this.mti == this.N+1) /* if init_seed() has not been called, */
|
||||
this.init_seed(5489); /* a default initial seed is used */
|
||||
|
||||
for (kk=0;kk<this.N-this.M;kk++) {
|
||||
y = (this.mt[kk]&this.UPPER_MASK)|(this.mt[kk+1]&this.LOWER_MASK);
|
||||
this.mt[kk] = this.mt[kk+this.M] ^ (y >>> 1) ^ mag01[y & 0x1];
|
||||
}
|
||||
for (;kk<this.N-1;kk++) {
|
||||
y = (this.mt[kk]&this.UPPER_MASK)|(this.mt[kk+1]&this.LOWER_MASK);
|
||||
this.mt[kk] = this.mt[kk+(this.M-this.N)] ^ (y >>> 1) ^ mag01[y & 0x1];
|
||||
}
|
||||
y = (this.mt[this.N-1]&this.UPPER_MASK)|(this.mt[0]&this.LOWER_MASK);
|
||||
this.mt[this.N-1] = this.mt[this.M-1] ^ (y >>> 1) ^ mag01[y & 0x1];
|
||||
|
||||
this.mti = 0;
|
||||
}
|
||||
|
||||
y = this.mt[this.mti++];
|
||||
|
||||
/* Tempering */
|
||||
y ^= (y >>> 11);
|
||||
y ^= (y << 7) & 0x9d2c5680;
|
||||
y ^= (y << 15) & 0xefc60000;
|
||||
y ^= (y >>> 18);
|
||||
|
||||
return y >>> 0;
|
||||
}
|
||||
|
||||
/* generates a random number on [0,0x7fffffff]-interval */
|
||||
/* origin name genrand_int31 */
|
||||
random_int31() {
|
||||
return (this.random_int()>>>1);
|
||||
}
|
||||
|
||||
/* generates a random number on [0,1]-real-interval */
|
||||
/* origin name genrand_real1 */
|
||||
random_incl() {
|
||||
return this.random_int()*(1.0/4294967295.0);
|
||||
/* divided by 2^32-1 */
|
||||
}
|
||||
|
||||
/* generates a random number on [0,1)-real-interval */
|
||||
random() {
|
||||
return this.random_int()*(1.0/4294967296.0);
|
||||
/* divided by 2^32 */
|
||||
}
|
||||
|
||||
/* generates a random number on (0,1)-real-interval */
|
||||
/* origin name genrand_real3 */
|
||||
random_excl() {
|
||||
return (this.random_int() + 0.5)*(1.0/4294967296.0);
|
||||
/* divided by 2^32 */
|
||||
}
|
||||
|
||||
/* generates a random number on [0,1) with 53-bit resolution*/
|
||||
/* origin name genrand_res53 */
|
||||
random_long() {
|
||||
var a=this.random_int()>>>5, b=this.random_int()>>>6;
|
||||
return(a*67108864.0+b)*(1.0/9007199254740992.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
24
src/manage/DataManage.ts
Normal file
24
src/manage/DataManage.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { IAccount } from "../data/DataModel";
|
||||
import { singleton } from "../decorator/singleton.decorator";
|
||||
|
||||
const LOCAL_ACCOUNT_DATAS = 'local_account_datas'
|
||||
@singleton
|
||||
export class DataManage{
|
||||
public loadData(){
|
||||
const dataStr = localStorage.getItem(LOCAL_ACCOUNT_DATAS)
|
||||
let result: IAccount[] = []
|
||||
if (dataStr) {
|
||||
try {
|
||||
result = JSON.parse(dataStr)
|
||||
} catch (err) {
|
||||
console.log('load local data error')
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public saveData(datas: IAccount[]) {
|
||||
const dataStr = JSON.stringify(datas)
|
||||
localStorage.setItem(LOCAL_ACCOUNT_DATAS, dataStr)
|
||||
}
|
||||
}
|
172
src/standards/ERC20Standard.ts
Normal file
172
src/standards/ERC20Standard.ts
Normal file
@ -0,0 +1,172 @@
|
||||
import Web3 from "web3";
|
||||
import { abiERC20 } from "../abis/abiERC20";
|
||||
import { BN, toUtf8 } from 'ethereumjs-util';
|
||||
|
||||
export class ERC20Standard {
|
||||
private web3: Web3;
|
||||
|
||||
constructor(web3: Web3) {
|
||||
this.web3 = web3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get balance or count for current account on specific asset contract.
|
||||
*
|
||||
* @param address - Asset ERC20 contract address.
|
||||
* @param selectedAddress - Current account public address.
|
||||
* @returns Promise resolving to BN object containing balance for current account on specific asset contract.
|
||||
*/
|
||||
async getBalanceOf(
|
||||
address: string,
|
||||
selectedAddress: string
|
||||
): Promise<BN> {
|
||||
const contract = new this.web3.eth.Contract(abiERC20, address);
|
||||
return new Promise<BN>((resolve, reject) => {
|
||||
contract.methods
|
||||
.balanceOf(selectedAddress)
|
||||
.call(
|
||||
{ from: selectedAddress },
|
||||
(error: Error, result: BN) => {
|
||||
/* istanbul ignore if */
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(result);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for the decimals for a given ERC20 asset.
|
||||
*
|
||||
* @param address - ERC20 asset contract string.
|
||||
* @returns Promise resolving to the 'decimals'.
|
||||
*/
|
||||
async getTokenDecimals(address: string): Promise<string> {
|
||||
const contract = new this.web3.eth.Contract(abiERC20, address);
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
contract.methods
|
||||
.decimals()
|
||||
.call((error: Error, result: BN | string) => {
|
||||
/* istanbul ignore if */
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(result.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for symbol for a given ERC20 asset.
|
||||
*
|
||||
* @param address - ERC20 asset contract address.
|
||||
* @returns Promise resolving to the 'symbol'.
|
||||
*/
|
||||
async getTokenSymbol(address: string): Promise<string> {
|
||||
const contract = new this.web3.eth.Contract(abiERC20, address);
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
contract.methods
|
||||
.symbol()
|
||||
.call((error: Error, result: BN | string) => {
|
||||
/* istanbul ignore if */
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(result.toString());
|
||||
});
|
||||
});
|
||||
// Signature for calling `symbol()`
|
||||
// const payload = { to: address, data: '0x95d89b41' };
|
||||
// return new Promise<string>((resolve, reject) => {
|
||||
// this.web3.eth.call(payload, undefined, (error: Error, result: string) => {
|
||||
// /* istanbul ignore if */
|
||||
// if (error) {
|
||||
// reject(error);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Parse as string
|
||||
// try {
|
||||
// const decoded = Web3.utils.hexToUtf8(result);
|
||||
// if (decoded) {
|
||||
// console.log('decoded')
|
||||
// resolve(decoded);
|
||||
// return;
|
||||
// }
|
||||
// } catch {
|
||||
// // Ignore error
|
||||
// }
|
||||
|
||||
// // Parse as bytes
|
||||
// try {
|
||||
// const utf8 = Web3.utils.toUtf8(result);
|
||||
// console.log('utf8')
|
||||
// resolve(utf8);
|
||||
// return;
|
||||
// } catch {
|
||||
// // Ignore error
|
||||
// }
|
||||
|
||||
// reject(new Error('Failed to parse token symbol'));
|
||||
// });
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
* Query if a contract implements an interface.
|
||||
*
|
||||
* @param address - Asset contract address.
|
||||
* @param userAddress - The public address for the currently active user's account.
|
||||
* @returns Promise resolving an object containing the standard, decimals, symbol and balance of the given contract/userAddress pair.
|
||||
*/
|
||||
async getDetails(
|
||||
address: string,
|
||||
userAddress?: string
|
||||
): Promise<{
|
||||
standard: string;
|
||||
symbol: string | undefined;
|
||||
decimals: string | undefined;
|
||||
balance: BN | undefined;
|
||||
}> {
|
||||
const [decimals, symbol] = await Promise.all([
|
||||
this.getTokenDecimals(address),
|
||||
this.getTokenSymbol(address),
|
||||
]);
|
||||
let balance;
|
||||
if (userAddress) {
|
||||
balance = await this.getBalanceOf(address, userAddress);
|
||||
}
|
||||
return {
|
||||
decimals,
|
||||
symbol,
|
||||
balance,
|
||||
standard: "ERC20",
|
||||
};
|
||||
}
|
||||
|
||||
async transfer({
|
||||
address,
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
gas,
|
||||
}: {
|
||||
address: string;
|
||||
from: string;
|
||||
to: string;
|
||||
amount: number | string;
|
||||
gas?: number;
|
||||
}) {
|
||||
const contract = new this.web3.eth.Contract(abiERC20, address);
|
||||
const amountBN = Web3.utils.toBN(Web3.utils.toWei(amount + ""));
|
||||
return contract.methods.transfer(to, amountBN).send({
|
||||
from,
|
||||
gas: gas || 1000000,
|
||||
});
|
||||
}
|
||||
}
|
288
src/standards/ERC721Standard.ts
Normal file
288
src/standards/ERC721Standard.ts
Normal file
@ -0,0 +1,288 @@
|
||||
|
||||
import Web3 from "web3";
|
||||
import { abiERC721 } from "../abis/abiERC721";
|
||||
import { timeoutFetch } from '../util/net.util';
|
||||
import { getFormattedIpfsUrl } from '../util/wallet.util';
|
||||
|
||||
export const ERC721 = 'ERC721';
|
||||
export const ERC721_INTERFACE_ID = '0x80ac58cd';
|
||||
export const ERC721_METADATA_INTERFACE_ID = '0x5b5e139f';
|
||||
export const ERC721_ENUMERABLE_INTERFACE_ID = '0x780e9d63';
|
||||
|
||||
export class ERC721Standard {
|
||||
private web3: Web3;
|
||||
|
||||
constructor(web3: Web3) {
|
||||
this.web3 = web3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query if contract implements ERC721Metadata interface.
|
||||
*
|
||||
* @param address - ERC721 asset contract address.
|
||||
* @returns Promise resolving to whether the contract implements ERC721Metadata interface.
|
||||
*/
|
||||
contractSupportsMetadataInterface = async (
|
||||
address: string,
|
||||
): Promise<boolean> => {
|
||||
return this.contractSupportsInterface(
|
||||
address,
|
||||
ERC721_METADATA_INTERFACE_ID,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Query if contract implements ERC721Enumerable interface.
|
||||
*
|
||||
* @param address - ERC721 asset contract address.
|
||||
* @returns Promise resolving to whether the contract implements ERC721Enumerable interface.
|
||||
*/
|
||||
contractSupportsEnumerableInterface = async (
|
||||
address: string,
|
||||
): Promise<boolean> => {
|
||||
return this.contractSupportsInterface(
|
||||
address,
|
||||
ERC721_ENUMERABLE_INTERFACE_ID,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Query if contract implements ERC721 interface.
|
||||
*
|
||||
* @param address - ERC721 asset contract address.
|
||||
* @returns Promise resolving to whether the contract implements ERC721 interface.
|
||||
*/
|
||||
contractSupportsBase721Interface = async (
|
||||
address: string,
|
||||
): Promise<boolean> => {
|
||||
return this.contractSupportsInterface(address, ERC721_INTERFACE_ID);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enumerate assets assigned to an owner.
|
||||
*
|
||||
* @param address - ERC721 asset contract address.
|
||||
* @param selectedAddress - Current account public address.
|
||||
* @param index - A collectible counter less than `balanceOf(selectedAddress)`.
|
||||
* @returns Promise resolving to token identifier for the 'index'th asset assigned to 'selectedAddress'.
|
||||
*/
|
||||
getCollectibleTokenId = async (
|
||||
address: string,
|
||||
selectedAddress: string,
|
||||
index: number,
|
||||
): Promise<string> => {
|
||||
const contract = new this.web3.eth.Contract(abiERC721, address);
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
contract.methods.tokenOfOwnerByIndex(
|
||||
selectedAddress,
|
||||
index).call((error: Error, result: string) => {
|
||||
/* istanbul ignore if */
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(result);
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
getBalance = async (
|
||||
address: string,
|
||||
selectedAddress: string
|
||||
): Promise<number> => {
|
||||
const contract = new this.web3.eth.Contract(abiERC721, address);
|
||||
return new Promise<number>((resolve, reject) => {
|
||||
contract.methods.balanceOf(
|
||||
selectedAddress).call((error: Error, result: number) => {
|
||||
/* istanbul ignore if */
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(result);
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Query for tokenURI for a given asset.
|
||||
*
|
||||
* @param address - ERC721 asset contract address.
|
||||
* @param tokenId - ERC721 asset identifier.
|
||||
* @returns Promise resolving to the 'tokenURI'.
|
||||
*/
|
||||
getTokenURI = async (address: string, tokenId: string): Promise<string> => {
|
||||
const contract = new this.web3.eth.Contract(abiERC721, address);
|
||||
const supportsMetadata = await this.contractSupportsMetadataInterface(
|
||||
address,
|
||||
);
|
||||
if (!supportsMetadata) {
|
||||
throw new Error('Contract does not support ERC721 metadata interface.');
|
||||
}
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
contract.methods.tokenURI(tokenId).call( (error: Error, result: string) => {
|
||||
/* istanbul ignore if */
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Query for name for a given asset.
|
||||
*
|
||||
* @param address - ERC721 asset contract address.
|
||||
* @returns Promise resolving to the 'name'.
|
||||
*/
|
||||
getAssetName = async (address: string): Promise<string> => {
|
||||
const contract = new this.web3.eth.Contract(abiERC721, address);
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
contract.methods.name().call((error: Error, result: string) => {
|
||||
/* istanbul ignore if */
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Query for symbol for a given asset.
|
||||
*
|
||||
* @param address - ERC721 asset contract address.
|
||||
* @returns Promise resolving to the 'symbol'.
|
||||
*/
|
||||
getAssetSymbol = async (address: string): Promise<string> => {
|
||||
const contract = new this.web3.eth.Contract(abiERC721, address);
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
contract.methods.symbol().call((error: Error, result: string) => {
|
||||
/* istanbul ignore if */
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Query for owner for a given ERC721 asset.
|
||||
*
|
||||
* @param address - ERC721 asset contract address.
|
||||
* @param tokenId - ERC721 asset identifier.
|
||||
* @returns Promise resolving to the owner address.
|
||||
*/
|
||||
async getOwnerOf(address: string, tokenId: string): Promise<string> {
|
||||
const contract = new this.web3.eth.Contract(abiERC721, address);
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
contract.methods.ownerOf(tokenId).call( (error: Error, result: string) => {
|
||||
/* istanbul ignore if */
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Query if a contract implements an interface.
|
||||
*
|
||||
* @param address - Asset contract address.
|
||||
* @param interfaceId - Interface identifier.
|
||||
* @returns Promise resolving to whether the contract implements `interfaceID`.
|
||||
*/
|
||||
private contractSupportsInterface = async (
|
||||
address: string,
|
||||
interfaceId: string,
|
||||
): Promise<boolean> => {
|
||||
const contract = new this.web3.eth.Contract(abiERC721, address);
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
contract.methods.supportsInterface(
|
||||
interfaceId).call((error: Error, result: boolean) => {
|
||||
/* istanbul ignore if */
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(result);
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Query if a contract implements an interface.
|
||||
*
|
||||
* @param address - Asset contract address.
|
||||
* @param ipfsGateway - The user's preferred IPFS gateway.
|
||||
* @param tokenId - tokenId of a given token in the contract.
|
||||
* @returns Promise resolving an object containing the standard, tokenURI, symbol and name of the given contract/tokenId pair.
|
||||
*/
|
||||
getDetails = async (
|
||||
address: string,
|
||||
ipfsGateway: string,
|
||||
tokenId?: string,
|
||||
): Promise<{
|
||||
standard: string;
|
||||
tokenURI: string | undefined;
|
||||
symbol: string | undefined;
|
||||
name: string | undefined;
|
||||
image: string | undefined;
|
||||
}> => {
|
||||
const isERC721 = await this.contractSupportsBase721Interface(address);
|
||||
if (!isERC721) {
|
||||
throw new Error("This isn't a valid ERC721 contract");
|
||||
}
|
||||
|
||||
let tokenURI, image, symbol, name;
|
||||
|
||||
// TODO upgrade to use Promise.allSettled for name/symbol when we can refactor to use es2020 in tsconfig
|
||||
try {
|
||||
symbol = await this.getAssetSymbol(address);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
name = await this.getAssetName(address);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (tokenId) {
|
||||
try {
|
||||
tokenURI = await this.getTokenURI(address, tokenId);
|
||||
if (tokenURI.startsWith('ipfs://')) {
|
||||
tokenURI = getFormattedIpfsUrl(ipfsGateway, tokenURI, true);
|
||||
}
|
||||
|
||||
const response = await timeoutFetch(tokenURI);
|
||||
const object = await response.json();
|
||||
image = object ? object.image : ''
|
||||
if (image.startsWith('ipfs://')) {
|
||||
image = getFormattedIpfsUrl(ipfsGateway, image, true);
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
standard: ERC721,
|
||||
tokenURI,
|
||||
symbol,
|
||||
name,
|
||||
image,
|
||||
};
|
||||
};
|
||||
}
|
45
src/util/chain.util.ts
Normal file
45
src/util/chain.util.ts
Normal file
@ -0,0 +1,45 @@
|
||||
|
||||
/**
|
||||
* change price with customer decimals to bigNum with 18 decimals
|
||||
* @param {number} price
|
||||
* @param {number} decimals
|
||||
* @return {string}
|
||||
*/
|
||||
export function parsePrice(price: number, decimals: number) {
|
||||
const n = 19 - decimals
|
||||
return price + new Array(n).join('0')
|
||||
}
|
||||
|
||||
/**
|
||||
* format price with customer decimals to string for display
|
||||
* @param {number | string} price
|
||||
* @param {number} decimals
|
||||
* @param {number} fixed
|
||||
* @return {number | string}
|
||||
*/
|
||||
export function formatPrice(price: number|string, decimals?: number, fixed = 2) {
|
||||
if (!decimals) {
|
||||
return price
|
||||
}
|
||||
let str = price + ''
|
||||
const length = str.length
|
||||
str = str.padStart(decimals, '0')
|
||||
if (decimals >= length) {
|
||||
str = '0.' + str
|
||||
} else {
|
||||
const pos = length - decimals
|
||||
str = str.slice(0, pos) + '.' + str.slice(pos)
|
||||
}
|
||||
str = str.slice(0, str.lastIndexOf('.') + fixed + 1)
|
||||
return str
|
||||
// return str.replace(/0+$/, '').replace(/\.+$/, '')
|
||||
}
|
||||
|
||||
/**
|
||||
* number to hex string
|
||||
* @param {number} chainId
|
||||
* @return {string}
|
||||
*/
|
||||
export function toHexChainId(chainId: number) {
|
||||
return '0x' + chainId.toString(16)
|
||||
}
|
126
src/util/net.util.ts
Normal file
126
src/util/net.util.ts
Normal file
@ -0,0 +1,126 @@
|
||||
import 'whatwg-fetch'
|
||||
|
||||
const TIMEOUT_ERROR = new Error("timeout");
|
||||
|
||||
const hexRe = /^[0-9A-Fa-f]+$/gu;
|
||||
|
||||
/**
|
||||
* Execute fetch and verify that the response was successful.
|
||||
*
|
||||
* @param request - Request information.
|
||||
* @param options - Fetch options.
|
||||
* @returns The fetch response.
|
||||
*/
|
||||
export async function successfulFetch(request: string, options?: RequestInit) {
|
||||
const response = await fetch(request, options);
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Fetch failed with status '${response.status}' for request '${request}'`
|
||||
);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute fetch and return object response.
|
||||
*
|
||||
* @param request - The request information.
|
||||
* @param options - The fetch options.
|
||||
* @returns The fetch response JSON data.
|
||||
*/
|
||||
export async function handleFetch(request: string, options?: RequestInit) {
|
||||
const response = await successfulFetch(request, options);
|
||||
const object = await response.json();
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute fetch and return object response, log if known error thrown, otherwise rethrow error.
|
||||
*
|
||||
* @param request - the request options object
|
||||
* @param request.url - The request url to query.
|
||||
* @param request.options - The fetch options.
|
||||
* @param request.timeout - Timeout to fail request
|
||||
* @param request.errorCodesToCatch - array of error codes for errors we want to catch in a particular context
|
||||
* @returns The fetch response JSON data or undefined (if error occurs).
|
||||
*/
|
||||
export async function fetchWithErrorHandling({
|
||||
url,
|
||||
options,
|
||||
timeout,
|
||||
errorCodesToCatch,
|
||||
}: {
|
||||
url: string;
|
||||
options?: RequestInit;
|
||||
timeout?: number;
|
||||
errorCodesToCatch?: number[];
|
||||
}) {
|
||||
let result;
|
||||
try {
|
||||
if (timeout) {
|
||||
result = Promise.race([
|
||||
await handleFetch(url, options),
|
||||
new Promise<Response>((_, reject) =>
|
||||
setTimeout(() => {
|
||||
reject(TIMEOUT_ERROR);
|
||||
}, timeout)
|
||||
),
|
||||
]);
|
||||
} else {
|
||||
result = await handleFetch(url, options);
|
||||
}
|
||||
} catch (e) {
|
||||
logOrRethrowError(e, errorCodesToCatch);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch that fails after timeout.
|
||||
*
|
||||
* @param url - Url to fetch.
|
||||
* @param options - Options to send with the request.
|
||||
* @param timeout - Timeout to fail request.
|
||||
* @returns Promise resolving the request.
|
||||
*/
|
||||
export async function timeoutFetch(
|
||||
url: string,
|
||||
options?: RequestInit,
|
||||
timeout = 500
|
||||
): Promise<Response> {
|
||||
return Promise.race([
|
||||
successfulFetch(url, options),
|
||||
new Promise<Response>((_, reject) =>
|
||||
setTimeout(() => {
|
||||
reject(TIMEOUT_ERROR);
|
||||
}, timeout)
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to log if error is a common fetch error and otherwise rethrow it.
|
||||
*
|
||||
* @param error - Caught error that we should either rethrow or log to console
|
||||
* @param codesToCatch - array of error codes for errors we want to catch and log in a particular context
|
||||
*/
|
||||
function logOrRethrowError(error: any, codesToCatch: number[] = []) {
|
||||
if (!error) {
|
||||
return;
|
||||
}
|
||||
|
||||
const includesErrorCodeToCatch = codesToCatch.some((code) =>
|
||||
error.message.includes(`Fetch failed with status '${code}'`)
|
||||
);
|
||||
|
||||
if (
|
||||
error instanceof Error &&
|
||||
(includesErrorCodeToCatch ||
|
||||
error.message.includes("Failed to fetch") ||
|
||||
error === TIMEOUT_ERROR)
|
||||
) {
|
||||
console.error(error);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
216
src/util/number.util.ts
Normal file
216
src/util/number.util.ts
Normal file
@ -0,0 +1,216 @@
|
||||
import Web3 from "web3";
|
||||
import { BN } from 'ethereumjs-util';
|
||||
|
||||
/**
|
||||
* Converts some token minimal unit to render format string, showing 5 decimals
|
||||
*
|
||||
* @param {Number|String|BN} tokenValue - Token value to convert
|
||||
* @param {Number} decimals - Token decimals to convert
|
||||
* @param {Number} decimalsToShow - Decimals to 5
|
||||
* @returns {String} - Number of token minimal unit, in render format
|
||||
* If value is less than 5 precision decimals will show '< 0.00001'
|
||||
*/
|
||||
export function renderFromTokenMinimalUnit(
|
||||
tokenValue,
|
||||
decimals,
|
||||
decimalsToShow = 5
|
||||
) {
|
||||
const minimalUnit = fromTokenMinimalUnit(tokenValue || 0, decimals);
|
||||
const minimalUnitNumber = parseFloat(minimalUnit);
|
||||
let renderMinimalUnit;
|
||||
if (minimalUnitNumber < 0.00001 && minimalUnitNumber > 0) {
|
||||
renderMinimalUnit = "< 0.00001";
|
||||
} else {
|
||||
const base = Math.pow(10, decimalsToShow);
|
||||
renderMinimalUnit = (
|
||||
Math.round(minimalUnitNumber * base) / base
|
||||
).toString();
|
||||
}
|
||||
return renderMinimalUnit;
|
||||
}
|
||||
/**
|
||||
* Converts token minimal unit to readable string value
|
||||
*
|
||||
* @param {number|string|Object} minimalInput - Token minimal unit to convert
|
||||
* @param {string} decimals - Token decimals to convert
|
||||
* @returns {string} - String containing the new number
|
||||
*/
|
||||
export function fromTokenMinimalUnit(minimalInput, decimals) {
|
||||
minimalInput = addHexPrefix(Number(minimalInput).toString(16));
|
||||
let minimal = safeNumberToBN(minimalInput);
|
||||
const negative = minimal.lt(new BN(0));
|
||||
const base = Web3.utils.toBN(Math.pow(10, decimals).toString());
|
||||
|
||||
if (negative) {
|
||||
minimal = minimal.mul(new BN(-1));
|
||||
}
|
||||
let fraction = minimal.mod(base).toString(10);
|
||||
while (fraction.length < decimals) {
|
||||
fraction = "0" + fraction;
|
||||
}
|
||||
fraction = fraction.match(/^([0-9]*[1-9]|0)(0*)/)[1];
|
||||
const whole = minimal.div(base).toString(10);
|
||||
let value = "" + whole + (fraction === "0" ? "" : "." + fraction);
|
||||
if (negative) {
|
||||
value = "-" + value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts wei to render format string, showing 5 decimals
|
||||
*
|
||||
* @param {Number|String|BN} value - Wei to convert
|
||||
* @param {Number} decimalsToShow - Decimals to 5
|
||||
* @returns {String} - Number of token minimal unit, in render format
|
||||
* If value is less than 5 precision decimals will show '< 0.00001'
|
||||
*/
|
||||
export function renderFromWei(value, decimalsToShow = 5) {
|
||||
let renderWei = '0';
|
||||
// avoid undefined
|
||||
if (value) {
|
||||
const wei = Web3.utils.fromWei(value);
|
||||
const weiNumber = parseFloat(wei);
|
||||
if (weiNumber < 0.00001 && weiNumber > 0) {
|
||||
renderWei = '< 0.00001';
|
||||
} else {
|
||||
const base = Math.pow(10, decimalsToShow);
|
||||
renderWei = (Math.round(weiNumber * base) / base).toString();
|
||||
}
|
||||
}
|
||||
return renderWei;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts token BN value to hex string number to be sent
|
||||
*
|
||||
* @param {Object} value - BN instance to convert
|
||||
* @param {number} decimals - Decimals to be considered on the conversion
|
||||
* @returns {string} - String of the hex token value
|
||||
*/
|
||||
export function calcTokenValueToSend(value, decimals) {
|
||||
return value ? (value * Math.pow(10, decimals)).toString(16) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a string is a valid decimal
|
||||
*
|
||||
* @param {string} value - String to check
|
||||
* @returns {boolean} - True if the string is a valid decimal
|
||||
*/
|
||||
export function isDecimal(value) {
|
||||
return (
|
||||
Number.isFinite(parseFloat(value)) &&
|
||||
!Number.isNaN(parseFloat(value)) &&
|
||||
!isNaN(+value)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BN object from a string
|
||||
*
|
||||
* @param {string} value - Some numeric value represented as a string
|
||||
* @returns {Object} - BN instance
|
||||
*/
|
||||
export function toBN(value) {
|
||||
return Web3.utils.toBN(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefixes a hex string with '0x' or '-0x' and returns it. Idempotent.
|
||||
*
|
||||
* @param {string} str - The string to prefix.
|
||||
* @returns {string} The prefixed string.
|
||||
*/
|
||||
export const addHexPrefix = (str: string) => {
|
||||
if (typeof str !== "string" || str.match(/^-?0x/u)) {
|
||||
return str;
|
||||
}
|
||||
|
||||
if (str.match(/^-?0X/u)) {
|
||||
return str.replace("0X", "0x");
|
||||
}
|
||||
|
||||
if (str.startsWith("-")) {
|
||||
return str.replace("-", "-0x");
|
||||
}
|
||||
|
||||
return `0x${str}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps 'numberToBN' method to avoid potential undefined and decimal values
|
||||
*
|
||||
* @param {number|string} value - number
|
||||
* @returns {Object} - The converted value as BN instance
|
||||
*/
|
||||
export function safeNumberToBN(value: number | string) {
|
||||
const safeValue = fastSplit(value.toString()) || "0";
|
||||
return numberToBN(safeValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a fast string split and returns the first item of the string based on the divider provided
|
||||
*
|
||||
* @param {number|string} value - number/string to be splitted
|
||||
* @param {string} divider - string value to use to split the string (default '.')
|
||||
* @returns {string} - the selected splitted element
|
||||
*/
|
||||
|
||||
export function fastSplit(value, divider = ".") {
|
||||
value += "";
|
||||
const [from, to] = [value.indexOf(divider), 0];
|
||||
return value.substring(from, to) || value;
|
||||
}
|
||||
|
||||
export function stripHexPrefix(str: string) {
|
||||
if (typeof str !== "string") {
|
||||
return str;
|
||||
}
|
||||
|
||||
return str.slice(0, 2) === "0x" ? str.slice(2) : str;
|
||||
}
|
||||
|
||||
export function numberToBN(arg) {
|
||||
if (typeof arg === "string" || typeof arg === "number") {
|
||||
var multiplier = Web3.utils.toBN(1); // eslint-disable-line
|
||||
var formattedString = String(arg).toLowerCase().trim();
|
||||
var isHexPrefixed =
|
||||
formattedString.substr(0, 2) === "0x" ||
|
||||
formattedString.substr(0, 3) === "-0x";
|
||||
var stringArg = stripHexPrefix(formattedString); // eslint-disable-line
|
||||
if (stringArg.substr(0, 1) === "-") {
|
||||
stringArg = stripHexPrefix(stringArg.slice(1));
|
||||
multiplier = Web3.utils.toBN(-1);
|
||||
}
|
||||
stringArg = stringArg === "" ? "0" : stringArg;
|
||||
|
||||
if (
|
||||
(!stringArg.match(/^-?[0-9]+$/) && stringArg.match(/^[0-9A-Fa-f]+$/)) ||
|
||||
stringArg.match(/^[a-fA-F]+$/) ||
|
||||
(isHexPrefixed === true && stringArg.match(/^[0-9A-Fa-f]+$/))
|
||||
) {
|
||||
return Web3.utils.toBN(stringArg).mul(multiplier);
|
||||
}
|
||||
|
||||
if (
|
||||
(stringArg.match(/^-?[0-9]+$/) || stringArg === "") &&
|
||||
isHexPrefixed === false
|
||||
) {
|
||||
return Web3.utils.toBN(stringArg).mul(multiplier);
|
||||
}
|
||||
} else if (typeof arg === "object" && arg.toString && !arg.pop && !arg.push) {
|
||||
if (
|
||||
arg.toString(10).match(/^-?[0-9]+$/) &&
|
||||
(arg.mul || arg.dividedToIntegerBy)
|
||||
) {
|
||||
return Web3.utils.toBN(arg.toString(10));
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"[number-to-bn] while converting number " +
|
||||
JSON.stringify(arg) +
|
||||
" to BN.js instance, error: invalid number value. Value must be an integer, hex string, BN or BigNumber instance. Note, decimals are not supported."
|
||||
);
|
||||
}
|
119
src/util/wallet.util.ts
Normal file
119
src/util/wallet.util.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import { renderFromTokenMinimalUnit } from "./number.util";
|
||||
|
||||
/**
|
||||
* Removes IPFS protocol prefix from input string.
|
||||
*
|
||||
* @param ipfsUrl - An IPFS url (e.g. ipfs://{content id})
|
||||
* @returns IPFS content identifier and (possibly) path in a string
|
||||
* @throws Will throw if the url passed is not IPFS.
|
||||
*/
|
||||
export function removeIpfsProtocolPrefix(ipfsUrl: string) {
|
||||
if (ipfsUrl.startsWith("ipfs://ipfs/")) {
|
||||
return ipfsUrl.replace("ipfs://ipfs/", "");
|
||||
} else if (ipfsUrl.startsWith("ipfs://")) {
|
||||
return ipfsUrl.replace("ipfs://", "");
|
||||
}
|
||||
// this method should not be used with non-ipfs urls (i.e. startsWith('ipfs://') === true)
|
||||
throw new Error("this method should not be used with non ipfs urls");
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts content identifier and path from an input string.
|
||||
*
|
||||
* @param ipfsUrl - An IPFS URL minus the IPFS protocol prefix
|
||||
* @returns IFPS content identifier (cid) and sub path as string.
|
||||
* @throws Will throw if the url passed is not ipfs.
|
||||
*/
|
||||
export function getIpfsCIDv1AndPath(ipfsUrl: string): {
|
||||
cid: string;
|
||||
path?: string;
|
||||
} {
|
||||
const url = removeIpfsProtocolPrefix(ipfsUrl);
|
||||
|
||||
// check if there is a path
|
||||
// (CID is everything preceding first forward slash, path is everything after)
|
||||
const index = url.indexOf("/");
|
||||
const cid = index !== -1 ? url.substring(0, index) : url;
|
||||
const path = index !== -1 ? url.substring(index) : undefined;
|
||||
//TODO:
|
||||
// We want to ensure that the CID is v1 (https://docs.ipfs.io/concepts/content-addressing/#identifier-formats)
|
||||
// because most cid v0s appear to be incompatible with IPFS subdomains
|
||||
// return {
|
||||
// cid: CID.parse(cid).toV1().toString(),
|
||||
// path,
|
||||
// };
|
||||
return {
|
||||
cid,
|
||||
path,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds URL protocol prefix to input URL string if missing.
|
||||
*
|
||||
* @param urlString - An IPFS URL.
|
||||
* @returns A URL with a https:// prepended.
|
||||
*/
|
||||
export function addUrlProtocolPrefix(urlString: string): string {
|
||||
if (!urlString.match(/(^http:\/\/)|(^https:\/\/)/u)) {
|
||||
return `https://${urlString}`;
|
||||
}
|
||||
return urlString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats URL correctly for use retrieving assets hosted on IPFS.
|
||||
*
|
||||
* @param ipfsGateway - The users preferred IPFS gateway (full URL or just host).
|
||||
* @param ipfsUrl - The IFPS URL pointed at the asset.
|
||||
* @param subdomainSupported - Boolean indicating whether the URL should be formatted with subdomains or not.
|
||||
* @returns A formatted URL, with the user's preferred IPFS gateway and format (subdomain or not), pointing to an asset hosted on IPFS.
|
||||
*/
|
||||
export function getFormattedIpfsUrl(
|
||||
ipfsGateway: string,
|
||||
ipfsUrl: string,
|
||||
subdomainSupported: boolean
|
||||
): string {
|
||||
const { host, protocol, origin } = new URL(addUrlProtocolPrefix(ipfsGateway));
|
||||
if (subdomainSupported) {
|
||||
const { cid, path } = getIpfsCIDv1AndPath(ipfsUrl);
|
||||
return `${protocol}//${cid}.ipfs.${host}${path || ""}`;
|
||||
}
|
||||
const cidAndPath = removeIpfsProtocolPrefix(ipfsUrl);
|
||||
return `${origin}/ipfs/${cidAndPath}`;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given code corresponds to a smart contract.
|
||||
*
|
||||
* @param code - The potential smart contract code.
|
||||
* @returns Whether the code was smart contract code or not.
|
||||
*/
|
||||
export function isSmartContractCode(code: string) {
|
||||
/* istanbul ignore if */
|
||||
if (!code) {
|
||||
return false;
|
||||
}
|
||||
// Geth will return '0x', and ganache-core v2.2.1 will return '0x0'
|
||||
const smartContractCode = code !== '0x' && code !== '0x0';
|
||||
return smartContractCode;
|
||||
}
|
||||
|
||||
export function formatAddress(address: string) {
|
||||
if (address.length >= 10) {
|
||||
return address.substring(0, 6) + '...' + address.substring(address.length - 4)
|
||||
} else if (address.length > 0 && address.length < 10) {
|
||||
return address
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export function formatMoney(balance: number | string, symbol: string) {
|
||||
if (balance === '-') {
|
||||
return `- ${symbol}`;
|
||||
}
|
||||
let money = renderFromTokenMinimalUnit(balance, 18, 4)
|
||||
return `${money} ${symbol}`;
|
||||
}
|
24
tsconfig.json
Normal file
24
tsconfig.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS",
|
||||
"rootDir": "src",
|
||||
"outDir": "build",
|
||||
"lib": [ "dom", "es5", "es2015", "es2015.promise" ],
|
||||
"target": "es5",
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"skipLibCheck": true,
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"library",
|
||||
"local",
|
||||
"temp",
|
||||
"build",
|
||||
"settings",
|
||||
"dist"
|
||||
]
|
||||
}
|
27
webpack.config.js
Normal file
27
webpack.config.js
Normal file
@ -0,0 +1,27 @@
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
mode: "production",
|
||||
// mode: 'development',
|
||||
entry: "./build/index.js",
|
||||
// devtool: "inline-source-map",
|
||||
target: "web",
|
||||
// module: {
|
||||
// rules: [
|
||||
// {
|
||||
// test: /\.ts?$/,
|
||||
// use: "ts-loader"
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// resolve: {
|
||||
// extensions: [".tsx", ".ts", ".js"],
|
||||
// },
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
filename: "index.js",
|
||||
library: "jcwallet",
|
||||
libraryTarget: "commonjs2",
|
||||
// libraryTarget: "window",
|
||||
},
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user