feat(mantle-wallet): complete Mantle wallet
Implemented full wallet functionality for Mantle:
- Balance Checking: Real-time balance fetching for MNT, WMNT, USDC, and USDT.
- Transaction History: Robust fetching via Mobula API with support for pagination and filtering.
- Transaction Details: Detailed view for individual transactions, including status, hash, and value.
- Sending Functionality: Secure MNT and token transfers using Type 0 (Legacy) transactions and L1 fee estimation.
- Key Management: Derivation of Mantle addresses and FLO IDs from private keys (WIF/Hex).
- Navigation Enhancements:
- Clickable addresses in history and details for seamless navigation.
This commit is contained in:
parent
9d823e9074
commit
72351dd5b8
32
.github/workflows/push-dappbundle.yml
vendored
Normal file
32
.github/workflows/push-dappbundle.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
name: Workflow push to Dappbundle
|
||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: self-hosted
|
||||||
|
steps:
|
||||||
|
- name: Executing remote command
|
||||||
|
uses: appleboy/ssh-action@v1.0.0
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.R_HOST }}
|
||||||
|
username: ${{ secrets.P_USERNAME }}
|
||||||
|
password: ${{ secrets.P_PASSWORD }}
|
||||||
|
port: ${{ secrets.SSH_PORT }}
|
||||||
|
script: |
|
||||||
|
if [ -d "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle" ]; then
|
||||||
|
echo "Folder exists. Skipping Git clone."
|
||||||
|
else
|
||||||
|
echo "Folder does not exist. Cloning repository..."
|
||||||
|
cd ${{ secrets.DEPLOYMENT_LOCATION}}/ && git clone https://github.com/ranchimall/dappbundle.git
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/${{ github.event.repository.name }}" ]; then
|
||||||
|
echo "Repository exists. Remove folder "
|
||||||
|
rm -r "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/${{ github.event.repository.name }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Cloning repository..."
|
||||||
|
cd ${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle && git clone https://github.com/ranchimall/${{ github.event.repository.name }}
|
||||||
|
|
||||||
|
cd "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/${{ github.event.repository.name }}" && rm -rf .gitattributes .git .github .gitignore
|
||||||
|
cd ${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/ && git add . && git commit -m "Workflow updating files of ${{ github.event.repository.name }}" && git push "https://saketongit:${{ secrets.RM_ACCESS_TOKEN }}@github.com/ranchimall/dappbundle.git"
|
||||||
1291
css/main.css
Normal file
1291
css/main.css
Normal file
File diff suppressed because it is too large
Load Diff
1995
index.html
Normal file
1995
index.html
Normal file
File diff suppressed because it is too large
Load Diff
1499
scripts/btcOperator.js
Normal file
1499
scripts/btcOperator.js
Normal file
File diff suppressed because it is too large
Load Diff
1
scripts/btcOperator.min.js
vendored
Normal file
1
scripts/btcOperator.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
10430
scripts/btcwallet_scripts_lib.js
Normal file
10430
scripts/btcwallet_scripts_lib.js
Normal file
File diff suppressed because it is too large
Load Diff
57
scripts/btcwallet_scripts_lib.min.js
vendored
Normal file
57
scripts/btcwallet_scripts_lib.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
257
scripts/compactIDB.js
Normal file
257
scripts/compactIDB.js
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
(function (EXPORTS) { //compactIDB v2.1.2
|
||||||
|
/* Compact IndexedDB operations */
|
||||||
|
'use strict';
|
||||||
|
const compactIDB = EXPORTS;
|
||||||
|
|
||||||
|
var defaultDB;
|
||||||
|
|
||||||
|
const indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
|
||||||
|
const IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
|
||||||
|
const IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
|
||||||
|
|
||||||
|
if (!indexedDB) {
|
||||||
|
console.error("Your browser doesn't support a stable version of IndexedDB.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.setDefaultDB = dbName => defaultDB = dbName;
|
||||||
|
|
||||||
|
Object.defineProperty(compactIDB, 'default', {
|
||||||
|
get: () => defaultDB,
|
||||||
|
set: dbName => defaultDB = dbName
|
||||||
|
});
|
||||||
|
|
||||||
|
function getDBversion(dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
resolve(db.version)
|
||||||
|
db.close()
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function upgradeDB(dbName, createList = null, deleteList = null) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
getDBversion(dbName).then(version => {
|
||||||
|
var idb = indexedDB.open(dbName, version + 1);
|
||||||
|
idb.onerror = (event) => reject("Error in opening IndexedDB");
|
||||||
|
idb.onupgradeneeded = (event) => {
|
||||||
|
let db = event.target.result;
|
||||||
|
if (createList instanceof Object) {
|
||||||
|
if (Array.isArray(createList)) {
|
||||||
|
let tmp = {}
|
||||||
|
createList.forEach(o => tmp[o] = {})
|
||||||
|
createList = tmp
|
||||||
|
}
|
||||||
|
for (let o in createList) {
|
||||||
|
let obs = db.createObjectStore(o, createList[o].options || {});
|
||||||
|
if (createList[o].indexes instanceof Object)
|
||||||
|
for (let i in createList[o].indexes)
|
||||||
|
obs.createIndex(i, i, createList[o].indexes || {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Array.isArray(deleteList))
|
||||||
|
deleteList.forEach(o => db.deleteObjectStore(o));
|
||||||
|
resolve('Database upgraded')
|
||||||
|
}
|
||||||
|
idb.onsuccess = (event) => event.target.result.close();
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.initDB = function (dbName, objectStores = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!(objectStores instanceof Object))
|
||||||
|
return reject('ObjectStores must be an object or array')
|
||||||
|
defaultDB = defaultDB || dbName;
|
||||||
|
var idb = indexedDB.open(dbName);
|
||||||
|
idb.onerror = (event) => reject("Error in opening IndexedDB");
|
||||||
|
idb.onsuccess = (event) => {
|
||||||
|
var db = event.target.result;
|
||||||
|
let cList = Object.values(db.objectStoreNames);
|
||||||
|
var obs = {},
|
||||||
|
a_obs = {},
|
||||||
|
d_obs = [];
|
||||||
|
if (!Array.isArray(objectStores))
|
||||||
|
var obs = objectStores
|
||||||
|
else
|
||||||
|
objectStores.forEach(o => obs[o] = {})
|
||||||
|
let nList = Object.keys(obs)
|
||||||
|
for (let o of nList)
|
||||||
|
if (!cList.includes(o))
|
||||||
|
a_obs[o] = obs[o]
|
||||||
|
for (let o of cList)
|
||||||
|
if (!nList.includes(o))
|
||||||
|
d_obs.push(o)
|
||||||
|
if (!Object.keys(a_obs).length && !d_obs.length)
|
||||||
|
resolve("Initiated IndexedDB");
|
||||||
|
else
|
||||||
|
upgradeDB(dbName, a_obs, d_obs)
|
||||||
|
.then(result => resolve(result))
|
||||||
|
.catch(error => reject(error))
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const openDB = compactIDB.openDB = function (dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var idb = indexedDB.open(dbName);
|
||||||
|
idb.onerror = (event) => reject("Error in opening IndexedDB");
|
||||||
|
idb.onupgradeneeded = (event) => {
|
||||||
|
event.target.result.close();
|
||||||
|
deleteDB(dbName).then(_ => null).catch(_ => null).finally(_ => reject("Datebase not found"))
|
||||||
|
}
|
||||||
|
idb.onsuccess = (event) => resolve(event.target.result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteDB = compactIDB.deleteDB = function (dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var deleteReq = indexedDB.deleteDatabase(dbName);;
|
||||||
|
deleteReq.onerror = (event) => reject("Error deleting database!");
|
||||||
|
deleteReq.onsuccess = (event) => resolve("Database deleted successfully");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.writeData = function (obsName, data, key = false, dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
||||||
|
let writeReq = (key ? obs.put(data, key) : obs.put(data));
|
||||||
|
writeReq.onsuccess = (evt) => resolve(`Write data Successful`);
|
||||||
|
writeReq.onerror = (evt) => reject(
|
||||||
|
`Write data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
||||||
|
);
|
||||||
|
db.close();
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.addData = function (obsName, data, key = false, dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
||||||
|
let addReq = (key ? obs.add(data, key) : obs.add(data));
|
||||||
|
addReq.onsuccess = (evt) => resolve(`Add data successful`);
|
||||||
|
addReq.onerror = (evt) => reject(
|
||||||
|
`Add data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
||||||
|
);
|
||||||
|
db.close();
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.removeData = function (obsName, key, dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
||||||
|
let delReq = obs.delete(key);
|
||||||
|
delReq.onsuccess = (evt) => resolve(`Removed Data ${key}`);
|
||||||
|
delReq.onerror = (evt) => reject(
|
||||||
|
`Remove data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
||||||
|
);
|
||||||
|
db.close();
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.clearData = function (obsName, dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
||||||
|
let clearReq = obs.clear();
|
||||||
|
clearReq.onsuccess = (evt) => resolve(`Clear data Successful`);
|
||||||
|
clearReq.onerror = (evt) => reject(`Clear data Unsuccessful`);
|
||||||
|
db.close();
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.readData = function (obsName, key, dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
||||||
|
let getReq = obs.get(key);
|
||||||
|
getReq.onsuccess = (evt) => resolve(evt.target.result);
|
||||||
|
getReq.onerror = (evt) => reject(
|
||||||
|
`Read data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
||||||
|
);
|
||||||
|
db.close();
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.readAllData = function (obsName, dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
||||||
|
var tmpResult = {}
|
||||||
|
let curReq = obs.openCursor();
|
||||||
|
curReq.onsuccess = (evt) => {
|
||||||
|
var cursor = evt.target.result;
|
||||||
|
if (cursor) {
|
||||||
|
tmpResult[cursor.primaryKey] = cursor.value;
|
||||||
|
cursor.continue();
|
||||||
|
} else
|
||||||
|
resolve(tmpResult);
|
||||||
|
}
|
||||||
|
curReq.onerror = (evt) => reject(
|
||||||
|
`Read-All data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
||||||
|
);
|
||||||
|
db.close();
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compactIDB.searchData = function (obsName, options = {}, dbName = defaultDB) {
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
||||||
|
var filteredResult = {}
|
||||||
|
let keyRange;
|
||||||
|
if(options.lowerKey!==null && options.upperKey!==null)
|
||||||
|
keyRange = IDBKeyRange.bound(options.lowerKey, options.upperKey);
|
||||||
|
else if(options.lowerKey!==null)
|
||||||
|
keyRange = IDBKeyRange.lowerBound(options.lowerKey);
|
||||||
|
else if (options.upperKey!==null)
|
||||||
|
keyRange = IDBKeyRange.upperBound(options.upperBound);
|
||||||
|
else if (options.atKey)
|
||||||
|
let curReq = obs.openCursor(keyRange, )
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
})
|
||||||
|
}*/
|
||||||
|
|
||||||
|
compactIDB.searchData = function (obsName, options = {}, dbName = defaultDB) {
|
||||||
|
options.lowerKey = options.atKey || options.lowerKey || 0
|
||||||
|
options.upperKey = options.atKey || options.upperKey || false
|
||||||
|
options.patternEval = options.patternEval || ((k, v) => true);
|
||||||
|
options.limit = options.limit || false;
|
||||||
|
options.reverse = options.reverse || false;
|
||||||
|
options.lastOnly = options.lastOnly || false
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
||||||
|
var filteredResult = {}
|
||||||
|
let curReq = obs.openCursor(
|
||||||
|
options.upperKey ? IDBKeyRange.bound(options.lowerKey, options.upperKey) : IDBKeyRange.lowerBound(options.lowerKey),
|
||||||
|
options.lastOnly || options.reverse ? "prev" : "next");
|
||||||
|
curReq.onsuccess = (evt) => {
|
||||||
|
var cursor = evt.target.result;
|
||||||
|
if (!cursor || (options.limit && options.limit <= Object.keys(filteredResult).length))
|
||||||
|
return resolve(filteredResult); //reached end of key list or limit reached
|
||||||
|
else if (options.patternEval(cursor.primaryKey, cursor.value)) {
|
||||||
|
filteredResult[cursor.primaryKey] = cursor.value;
|
||||||
|
options.lastOnly ? resolve(filteredResult) : cursor.continue();
|
||||||
|
} else
|
||||||
|
cursor.continue();
|
||||||
|
}
|
||||||
|
curReq.onerror = (evt) => reject(`Search unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`);
|
||||||
|
db.close();
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})(window.compactIDB = {});
|
||||||
9
scripts/components.js
Normal file
9
scripts/components.js
Normal file
File diff suppressed because one or more lines are too long
1
scripts/components.min.js
vendored
Normal file
1
scripts/components.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
592
scripts/ethOperator.js
Normal file
592
scripts/ethOperator.js
Normal file
@ -0,0 +1,592 @@
|
|||||||
|
(function (EXPORTS) {
|
||||||
|
/**
|
||||||
|
* ethOperator.js
|
||||||
|
*
|
||||||
|
* Core logic for interacting with the Mantle network.
|
||||||
|
* Handles address validation, balance checks, gas estimation (the hard part),
|
||||||
|
* and transaction management for both MNT and ERC20 tokens.
|
||||||
|
*/
|
||||||
|
if (!window.ethers)
|
||||||
|
return console.error('ethers.js not found')
|
||||||
|
const ethOperator = EXPORTS;
|
||||||
|
const isValidAddress = ethOperator.isValidAddress = (address) => {
|
||||||
|
try {
|
||||||
|
// We verify both checksummed and non-checksummed addresses to be user-friendly.
|
||||||
|
// Some scanners/explorers might provide lowercased addresses.
|
||||||
|
const isValidChecksum = ethers.utils.isAddress(address);
|
||||||
|
const isValidNonChecksum = ethers.utils.getAddress(address) === address.toLowerCase();
|
||||||
|
return isValidChecksum || isValidNonChecksum;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const ERC20ABI = [
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "name",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_spender",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "approve",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"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": "_value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "transferFrom",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "decimals",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_owner",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "balanceOf",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "balance",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "symbol",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_to",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_value",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "transfer",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "_owner",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_spender",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "allowance",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"payable": true,
|
||||||
|
"stateMutability": "payable",
|
||||||
|
"type": "fallback"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
const CONTRACT_ADDRESSES = {
|
||||||
|
usdc: "0x09Bc4E0D864854c6aFB6eB9A9cdF58aC190D0dF9",
|
||||||
|
usdt: "0x201EBa5CC46D216Ce6DC03F6a759e8E766e956aE",
|
||||||
|
wmnt: "0x78c1b0C915c4FAA5FffA6CAbf0219DA63d7f4cb8"
|
||||||
|
}
|
||||||
|
|
||||||
|
const MANTLE_GAS_ORACLE = "0x420000000000000000000000000000000000000F";
|
||||||
|
const GAS_ORACLE_ABI = [
|
||||||
|
"function getL1Fee(bytes data) view returns (uint256)",
|
||||||
|
"function l1BaseFee() view returns (uint256)"
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* Determines which provider to use.
|
||||||
|
* By default, we prefer the Public RPC for balance checks to avoid
|
||||||
|
* unnecessary MetaMask population/permissions.
|
||||||
|
*/
|
||||||
|
const getProvider = ethOperator.getProvider = (readOnly = false) => {
|
||||||
|
if (!readOnly && window.ethereum) {
|
||||||
|
return new ethers.providers.Web3Provider(window.ethereum);
|
||||||
|
} else {
|
||||||
|
return new ethers.providers.JsonRpcProvider(`https://rpc.mantle.xyz`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Note: Connection logic is managed in index.html, we just handle the pipe here.
|
||||||
|
const getBalance = ethOperator.getBalance = async (address) => {
|
||||||
|
try {
|
||||||
|
if (!address || !isValidAddress(address))
|
||||||
|
return new Error('Invalid address');
|
||||||
|
|
||||||
|
// Use read-only provider (public RPC) for balance checks
|
||||||
|
const provider = getProvider(true);
|
||||||
|
const balanceWei = await provider.getBalance(address);
|
||||||
|
const balanceEth = parseFloat(ethers.utils.formatEther(balanceWei));
|
||||||
|
return balanceEth;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Balance error:', error.message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const getTokenBalance = ethOperator.getTokenBalance = async (address, token, { contractAddress } = {}) => {
|
||||||
|
try {
|
||||||
|
if (!token)
|
||||||
|
return new Error("Token not specified");
|
||||||
|
if (!CONTRACT_ADDRESSES[token] && !contractAddress)
|
||||||
|
return new Error('Contract address of token not available')
|
||||||
|
|
||||||
|
// Fetching via Public RPC for speed and to avoid MetaMask prompts.
|
||||||
|
const provider = getProvider(true);
|
||||||
|
const tokenAddress = CONTRACT_ADDRESSES[token] || contractAddress;
|
||||||
|
const tokenContract = new ethers.Contract(tokenAddress, ERC20ABI, provider);
|
||||||
|
let balance = await tokenContract.balanceOf(address);
|
||||||
|
|
||||||
|
// Tokens like USDC/USDT use 6 decimals, while WMNT/MNT use 18.
|
||||||
|
const decimals = token === 'wmnt' ? 18 : 6;
|
||||||
|
balance = parseFloat(ethers.utils.formatUnits(balance, decimals));
|
||||||
|
return balance;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Token balance error:', e);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const estimateGas = ethOperator.estimateGas = async ({ privateKey, receiver, amount }) => {
|
||||||
|
try {
|
||||||
|
const provider = getProvider();
|
||||||
|
const signer = new ethers.Wallet(privateKey, provider);
|
||||||
|
|
||||||
|
let gasLimit;
|
||||||
|
try {
|
||||||
|
gasLimit = await provider.estimateGas({
|
||||||
|
from: signer.address,
|
||||||
|
to: receiver,
|
||||||
|
value: ethers.utils.parseUnits(amount, "ether"),
|
||||||
|
});
|
||||||
|
} catch (estimateError) {
|
||||||
|
// If RPC estimation fails (often due to balance or sequencer check),
|
||||||
|
// we use a block-limit fallback that triggers our secondary check below.
|
||||||
|
console.warn('Gas estimation RPC call failed, using fallback:', estimateError.message);
|
||||||
|
gasLimit = ethers.BigNumber.from("89000000");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MANTLE SPECIFIC: Sequence Logic
|
||||||
|
* The sequencer often returns the block limit (~89M) if it thinks the tx will fail
|
||||||
|
* or if the gas is below the L1 requirement. To satisfy the "intrinsic gas" check,
|
||||||
|
* we calculate the limit relative to the L1 Base Fee.
|
||||||
|
*
|
||||||
|
* A threshold of 80M is used to identify these "fake" estimates from the sequencer.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
const gasLimitValue = BigInt(gasLimit.toString());
|
||||||
|
if (gasLimitValue > 1000000n) {
|
||||||
|
const oracle = new ethers.Contract(MANTLE_GAS_ORACLE, GAS_ORACLE_ABI, getProvider(true));
|
||||||
|
const l1BaseFee = await oracle.l1BaseFee();
|
||||||
|
|
||||||
|
if (l1BaseFee.gt(21000)) {
|
||||||
|
// 80M is a safe "super buffer" threshold for the Mantle sequencer.
|
||||||
|
const minLimit = ethers.BigNumber.from("80000000");
|
||||||
|
const fallbackLimit = l1BaseFee.gt(minLimit) ? l1BaseFee.add(1000000) : minLimit;
|
||||||
|
return fallbackLimit;
|
||||||
|
}
|
||||||
|
return ethers.BigNumber.from("21000");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('Error in gas limit fallback logic:', err);
|
||||||
|
if (gasLimit.gt(1000000)) return ethers.BigNumber.from("21000");
|
||||||
|
}
|
||||||
|
return gasLimit;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Gas estimation failed completely, using default 21000', e);
|
||||||
|
return ethers.BigNumber.from("21000");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Mantle L1 fee based on transaction data
|
||||||
|
* @param {string} data - The hex transaction data
|
||||||
|
* @returns {Promise<ethers.BigNumber>} - The estimated L1 fee in Wei
|
||||||
|
*/
|
||||||
|
const getL1Fee = ethOperator.getL1Fee = async (data = "0x") => {
|
||||||
|
try {
|
||||||
|
const provider = getProvider(true);
|
||||||
|
const oracle = new ethers.Contract(MANTLE_GAS_ORACLE, GAS_ORACLE_ABI, provider);
|
||||||
|
return await oracle.getL1Fee(data);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('L1 Fee estimation error:', e);
|
||||||
|
return ethers.BigNumber.from("0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendTransaction = ethOperator.sendTransaction = async ({ privateKey, receiver, amount }) => {
|
||||||
|
try {
|
||||||
|
const provider = getProvider();
|
||||||
|
const signer = new ethers.Wallet(privateKey, provider);
|
||||||
|
const limit = await estimateGas({ privateKey, receiver, amount })
|
||||||
|
|
||||||
|
const gasPrice = await provider.getGasPrice();
|
||||||
|
|
||||||
|
// We force Legacy (Type 0) transactions here.
|
||||||
|
// Mantle's L2 infrastructure is currently more stable with legacy transactions
|
||||||
|
// than with EIP-1559 due to specific L1 fee rollup logic.
|
||||||
|
return signer.sendTransaction({
|
||||||
|
to: receiver,
|
||||||
|
value: ethers.utils.parseUnits(amount, "ether"),
|
||||||
|
gasLimit: limit,
|
||||||
|
nonce: await signer.getTransactionCount(),
|
||||||
|
gasPrice: gasPrice,
|
||||||
|
type: 0
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send ERC20 tokens (USDC, USDT, or WMNT)
|
||||||
|
* @param {object} params - Transaction parameters
|
||||||
|
* @param {string} params.token - Token symbol ('usdc', 'usdt', or 'wmnt')
|
||||||
|
* @param {string} params.privateKey - Sender's private key
|
||||||
|
* @param {string} params.amount - Amount to send
|
||||||
|
* @param {string} params.receiver - Recipient's Mantle address
|
||||||
|
* @param {string} params.contractAddress - Optional custom contract address
|
||||||
|
* @returns {Promise} Transaction promise
|
||||||
|
*/
|
||||||
|
const sendToken = ethOperator.sendToken = async ({ token, privateKey, amount, receiver, contractAddress }) => {
|
||||||
|
const wallet = new ethers.Wallet(privateKey, getProvider());
|
||||||
|
const tokenContract = new ethers.Contract(CONTRACT_ADDRESSES[token] || contractAddress, ERC20ABI, wallet);
|
||||||
|
// Convert amount to smallest unit: WMNT uses 18 decimals, USDC and USDT use 6 decimals
|
||||||
|
const decimals = token === 'wmnt' ? 18 : 6;
|
||||||
|
const amountWei = ethers.utils.parseUnits(amount.toString(), decimals);
|
||||||
|
return tokenContract.transfer(receiver, amountWei)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get transaction history for a Mantle address using Mobula API
|
||||||
|
* Free API with full transaction history support
|
||||||
|
* @param {string} address - Mantle address
|
||||||
|
* @param {object} options - Optional parameters
|
||||||
|
* @returns {Promise<Array>} Array of transactions
|
||||||
|
*/
|
||||||
|
const getTransactionHistory = ethOperator.getTransactionHistory = async (address, options = {}) => {
|
||||||
|
try {
|
||||||
|
if (!address || !isValidAddress(address)) {
|
||||||
|
throw new Error('Invalid Mantle address');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { page = 1, offset = 50 } = options;
|
||||||
|
|
||||||
|
// We use Mobula API because it provides the best free-tier support for Mantle history.
|
||||||
|
const MOBULA_API_URL = 'https://api.mobula.io/api/1/wallet/transactions';
|
||||||
|
const url = `${MOBULA_API_URL}?wallet=${address}&blockchain=mantle&limit=${offset}&offset=${(page - 1) * offset}`;
|
||||||
|
|
||||||
|
|
||||||
|
const response = await fetch(url);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.warn(`Mobula API returned ${response.status}`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!data.data || !data.data.transactions || data.data.transactions.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalizing Mobula data to our unified transaction format.
|
||||||
|
const transactions = data.data.transactions
|
||||||
|
.filter(tx => tx.blockchain && tx.blockchain.toLowerCase() === 'mantle')
|
||||||
|
.map(tx => {
|
||||||
|
const isReceived = tx.to && tx.to.toLowerCase() === address.toLowerCase();
|
||||||
|
|
||||||
|
// tx_cost is returned in MNT by Mobula, representing the total fee paid.
|
||||||
|
const txFee = parseFloat(tx.tx_cost || 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
hash: tx.hash,
|
||||||
|
from: tx.from,
|
||||||
|
to: tx.to,
|
||||||
|
value: parseFloat(tx.amount || 0),
|
||||||
|
symbol: tx.asset?.symbol || 'MNT',
|
||||||
|
timestamp: Math.floor(tx.timestamp / 1000),
|
||||||
|
blockNumber: tx.block_number,
|
||||||
|
isReceived: isReceived,
|
||||||
|
isSent: !isReceived,
|
||||||
|
gasUsed: txFee > 0 ? 21000 : 0,
|
||||||
|
gasPrice: txFee > 0 ? (txFee / 21000) * 1e18 : 0,
|
||||||
|
transactionFee: txFee,
|
||||||
|
isError: false,
|
||||||
|
contractAddress: tx.contract !== '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' ? tx.contract : null,
|
||||||
|
tokenName: tx.asset?.name || null,
|
||||||
|
confirmations: 0,
|
||||||
|
nonce: 0,
|
||||||
|
input: '0x',
|
||||||
|
isTokenTransfer: tx.type !== 'native',
|
||||||
|
amountUSD: tx.amount_usd,
|
||||||
|
txType: tx.type,
|
||||||
|
txCost: tx.tx_cost
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return transactions;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching transaction history:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get detailed information about a specific transaction
|
||||||
|
* @param {string} txHash - Transaction hash
|
||||||
|
* @returns {Promise<Object>} Transaction details
|
||||||
|
*/
|
||||||
|
const getTransactionDetails = ethOperator.getTransactionDetails = async (txHash) => {
|
||||||
|
try {
|
||||||
|
if (!txHash || !/^0x([A-Fa-f0-9]{64})$/.test(txHash)) {
|
||||||
|
throw new Error('Invalid transaction hash');
|
||||||
|
}
|
||||||
|
|
||||||
|
const provider = getProvider(true);
|
||||||
|
const tx = await provider.getTransaction(txHash);
|
||||||
|
|
||||||
|
if (!tx) {
|
||||||
|
throw new Error('Transaction not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const receipt = await provider.getTransactionReceipt(txHash);
|
||||||
|
const currentBlock = await provider.getBlockNumber();
|
||||||
|
const block = await provider.getBlock(tx.blockNumber);
|
||||||
|
|
||||||
|
const gasUsed = receipt ? receipt.gasUsed : null;
|
||||||
|
const effectiveGasPrice = receipt ? receipt.effectiveGasPrice : tx.gasPrice;
|
||||||
|
const gasFee = gasUsed && effectiveGasPrice ?
|
||||||
|
parseFloat(ethers.utils.formatEther(gasUsed.mul(effectiveGasPrice))) : null;
|
||||||
|
|
||||||
|
// Decoding ERC20 transfers by looking for the standard Transfer(address,address,uint256) event.
|
||||||
|
let tokenTransfer = null;
|
||||||
|
if (receipt && receipt.logs.length > 0) {
|
||||||
|
const transferEventSignature = ethers.utils.id('Transfer(address,address,uint256)');
|
||||||
|
const transferLog = receipt.logs.find(log => log.topics[0] === transferEventSignature);
|
||||||
|
|
||||||
|
if (transferLog) {
|
||||||
|
try {
|
||||||
|
const tokenContract = new ethers.Contract(transferLog.address, ERC20ABI, provider);
|
||||||
|
const [symbol, decimals] = await Promise.all([
|
||||||
|
tokenContract.symbol().catch(() => 'TOKEN'),
|
||||||
|
tokenContract.decimals().catch(() => 18)
|
||||||
|
]);
|
||||||
|
|
||||||
|
const from = ethers.utils.getAddress('0x' + transferLog.topics[1].slice(26));
|
||||||
|
const to = ethers.utils.getAddress('0x' + transferLog.topics[2].slice(26));
|
||||||
|
const value = parseFloat(ethers.utils.formatUnits(transferLog.data, decimals));
|
||||||
|
|
||||||
|
tokenTransfer = {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
value,
|
||||||
|
symbol,
|
||||||
|
contractAddress: transferLog.address
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Could not decode token transfer event:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
hash: tx.hash,
|
||||||
|
from: tx.from,
|
||||||
|
to: tx.to,
|
||||||
|
value: parseFloat(ethers.utils.formatEther(tx.value)),
|
||||||
|
symbol: 'MNT',
|
||||||
|
blockNumber: tx.blockNumber,
|
||||||
|
timestamp: block ? block.timestamp : null,
|
||||||
|
confirmations: currentBlock - tx.blockNumber,
|
||||||
|
gasLimit: tx.gasLimit.toString(),
|
||||||
|
gasUsed: gasUsed ? gasUsed.toString() : null,
|
||||||
|
gasPrice: parseFloat(ethers.utils.formatUnits(tx.gasPrice, 'gwei')),
|
||||||
|
gasFee: gasFee,
|
||||||
|
nonce: tx.nonce,
|
||||||
|
input: tx.data,
|
||||||
|
status: receipt ? (receipt.status === 1 ? 'success' : 'failed') : 'pending',
|
||||||
|
isError: receipt ? receipt.status !== 1 : false,
|
||||||
|
tokenTransfer: tokenTransfer,
|
||||||
|
logs: receipt ? receipt.logs : [],
|
||||||
|
type: tx.type
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching deep transaction details:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a string is a valid transaction hash
|
||||||
|
* @param {string} hash - Potential transaction hash
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
const isValidTxHash = ethOperator.isValidTxHash = (hash) => {
|
||||||
|
return /^0x([A-Fa-f0-9]{64})$/.test(hash);
|
||||||
|
};
|
||||||
|
|
||||||
|
})('object' === typeof module ? module.exports : window.ethOperator = {});
|
||||||
1
scripts/ethOperator.min.js
vendored
Normal file
1
scripts/ethOperator.min.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
!function(EXPORTS){if(!window.ethers)return console.error("ethers.js not found");const ethOperator=EXPORTS,isValidAddress=ethOperator.isValidAddress=address=>{try{const isValidChecksum=ethers.utils.isAddress(address),isValidNonChecksum=ethers.utils.getAddress(address)===address.toLowerCase();return isValidChecksum||isValidNonChecksum}catch(error){return!1}},ERC20ABI=[{constant:!0,inputs:[],name:"name",outputs:[{name:"",type:"string"}],payable:!1,stateMutability:"view",type:"function"},{constant:!1,inputs:[{name:"_spender",type:"address"},{name:"_value",type:"uint256"}],name:"approve",outputs:[{name:"",type:"bool"}],payable:!1,stateMutability:"nonpayable",type:"function"},{constant:!0,inputs:[],name:"totalSupply",outputs:[{name:"",type:"uint256"}],payable:!1,stateMutability:"view",type:"function"},{constant:!1,inputs:[{name:"_from",type:"address"},{name:"_to",type:"address"},{name:"_value",type:"uint256"}],name:"transferFrom",outputs:[{name:"",type:"bool"}],payable:!1,stateMutability:"nonpayable",type:"function"},{constant:!0,inputs:[],name:"decimals",outputs:[{name:"",type:"uint8"}],payable:!1,stateMutability:"view",type:"function"},{constant:!0,inputs:[{name:"_owner",type:"address"}],name:"balanceOf",outputs:[{name:"balance",type:"uint256"}],payable:!1,stateMutability:"view",type:"function"},{constant:!0,inputs:[],name:"symbol",outputs:[{name:"",type:"string"}],payable:!1,stateMutability:"view",type:"function"},{constant:!1,inputs:[{name:"_to",type:"address"},{name:"_value",type:"uint256"}],name:"transfer",outputs:[{name:"",type:"bool"}],payable:!1,stateMutability:"nonpayable",type:"function"},{constant:!0,inputs:[{name:"_owner",type:"address"},{name:"_spender",type:"address"}],name:"allowance",outputs:[{name:"",type:"uint256"}],payable:!1,stateMutability:"view",type:"function"},{payable:!0,stateMutability:"payable",type:"fallback"},{anonymous:!1,inputs:[{indexed:!0,name:"owner",type:"address"},{indexed:!0,name:"spender",type:"address"},{indexed:!1,name:"value",type:"uint256"}],name:"Approval",type:"event"},{anonymous:!1,inputs:[{indexed:!0,name:"from",type:"address"},{indexed:!0,name:"to",type:"address"},{indexed:!1,name:"value",type:"uint256"}],name:"Transfer",type:"event"}],CONTRACT_ADDRESSES={usdc:"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",usdt:"0xdac17f958d2ee523a2206206994597c13d831ec7"};function getProvider(){return window.ethereum?new ethers.providers.Web3Provider(window.ethereum):new ethers.providers.JsonRpcProvider("https://mainnet.infura.io/v3/6e12fee52bdd48208f0d82fb345bcb3c")}ethOperator.getBalance=async address=>{try{if(!address||!isValidAddress(address))return new Error("Invalid address");const provider=getProvider(),balanceWei=await provider.getBalance(address);return parseFloat(ethers.utils.formatEther(balanceWei))}catch(error){return console.error("Error:",error.message),error}},ethOperator.getTokenBalance=async(address,token,{contractAddress:contractAddress}={})=>{try{if(!token)return new Error("Token not specified");if(!CONTRACT_ADDRESSES[token]&&contractAddress)return new Error("Contract address of token not available");const usdcContract=new ethers.Contract(CONTRACT_ADDRESSES[token]||contractAddress,ERC20ABI,getProvider());let balance=await usdcContract.balanceOf(address);return balance=parseFloat(ethers.utils.formatUnits(balance,6)),balance}catch(e){console.error(e)}};const estimateGas=ethOperator.estimateGas=async({privateKey:privateKey,receiver:receiver,amount:amount})=>{try{const provider=getProvider(),signer=new ethers.Wallet(privateKey,provider);return provider.estimateGas({from:signer.address,to:receiver,value:ethers.utils.parseUnits(amount,"ether")})}catch(e){throw new Error(e)}};ethOperator.sendTransaction=async({privateKey:privateKey,receiver:receiver,amount:amount})=>{try{const provider=getProvider(),signer=new ethers.Wallet(privateKey,provider),limit=await estimateGas({privateKey:privateKey,receiver:receiver,amount:amount});return signer.sendTransaction({to:receiver,value:ethers.utils.parseUnits(amount,"ether"),gasLimit:limit,nonce:signer.getTransactionCount(),maxPriorityFeePerGas:ethers.utils.parseUnits("2","gwei")})}catch(e){throw new Error(e)}},ethOperator.sendToken=async({token:token,privateKey:privateKey,amount:amount,receiver:receiver,contractAddress:contractAddress})=>{const wallet=new ethers.Wallet(privateKey,getProvider()),tokenContract=new ethers.Contract(CONTRACT_ADDRESSES[token]||contractAddress,ERC20ABI,wallet),amountWei=ethers.utils.parseUnits(amount.toString(),6);return tokenContract.transfer(receiver,amountWei)}}("object"==typeof module?module.exports:window.ethOperator={});
|
||||||
1
scripts/ether.umd.min.js
vendored
Normal file
1
scripts/ether.umd.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
530
scripts/floCrypto.js
Normal file
530
scripts/floCrypto.js
Normal file
@ -0,0 +1,530 @@
|
|||||||
|
(function (EXPORTS) { //floCrypto v2.3.6a
|
||||||
|
/* FLO Crypto Operators */
|
||||||
|
'use strict';
|
||||||
|
const floCrypto = EXPORTS;
|
||||||
|
|
||||||
|
const p = BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);
|
||||||
|
const ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
||||||
|
const ascii_alternatives = `‘ '\n’ '\n“ "\n” "\n– --\n— ---\n≥ >=\n≤ <=\n≠ !=\n× *\n÷ /\n← <-\n→ ->\n↔ <->\n⇒ =>\n⇐ <=\n⇔ <=>`;
|
||||||
|
const exponent1 = () => p.add(BigInteger.ONE).divide(BigInteger("4"));
|
||||||
|
coinjs.compressed = true; //defaulting coinjs compressed to true;
|
||||||
|
|
||||||
|
function calculateY(x) {
|
||||||
|
let exp = exponent1();
|
||||||
|
// x is x value of public key in BigInteger format without 02 or 03 or 04 prefix
|
||||||
|
return x.modPow(BigInteger("3"), p).add(BigInteger("7")).mod(p).modPow(exp, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUncompressedPublicKey(compressedPublicKey) {
|
||||||
|
// Fetch x from compressedPublicKey
|
||||||
|
let pubKeyBytes = Crypto.util.hexToBytes(compressedPublicKey);
|
||||||
|
const prefix = pubKeyBytes.shift() // remove prefix
|
||||||
|
let prefix_modulus = prefix % 2;
|
||||||
|
pubKeyBytes.unshift(0) // add prefix 0
|
||||||
|
let x = new BigInteger(pubKeyBytes)
|
||||||
|
let xDecimalValue = x.toString()
|
||||||
|
// Fetch y
|
||||||
|
let y = calculateY(x);
|
||||||
|
let yDecimalValue = y.toString();
|
||||||
|
// verify y value
|
||||||
|
let resultBigInt = y.mod(BigInteger("2"));
|
||||||
|
let check = resultBigInt.toString() % 2;
|
||||||
|
if (prefix_modulus !== check)
|
||||||
|
yDecimalValue = y.negate().mod(p).toString();
|
||||||
|
return {
|
||||||
|
x: xDecimalValue,
|
||||||
|
y: yDecimalValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSenderPublicKeyString() {
|
||||||
|
let privateKey = ellipticCurveEncryption.senderRandom();
|
||||||
|
var senderPublicKeyString = ellipticCurveEncryption.senderPublicString(privateKey);
|
||||||
|
return {
|
||||||
|
privateKey: privateKey,
|
||||||
|
senderPublicKeyString: senderPublicKeyString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deriveSharedKeySender(receiverPublicKeyHex, senderPrivateKey) {
|
||||||
|
let receiverPublicKeyString = getUncompressedPublicKey(receiverPublicKeyHex);
|
||||||
|
var senderDerivedKey = ellipticCurveEncryption.senderSharedKeyDerivation(
|
||||||
|
receiverPublicKeyString.x, receiverPublicKeyString.y, senderPrivateKey);
|
||||||
|
return senderDerivedKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deriveSharedKeyReceiver(senderPublicKeyString, receiverPrivateKey) {
|
||||||
|
return ellipticCurveEncryption.receiverSharedKeyDerivation(
|
||||||
|
senderPublicKeyString.XValuePublicString, senderPublicKeyString.YValuePublicString, receiverPrivateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReceiverPublicKeyString(privateKey) {
|
||||||
|
return ellipticCurveEncryption.receiverPublicString(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
function wifToDecimal(pk_wif, isPubKeyCompressed = false) {
|
||||||
|
let pk = Bitcoin.Base58.decode(pk_wif)
|
||||||
|
pk.shift()
|
||||||
|
pk.splice(-4, 4)
|
||||||
|
//If the private key corresponded to a compressed public key, also drop the last byte (it should be 0x01).
|
||||||
|
if (isPubKeyCompressed == true) pk.pop()
|
||||||
|
pk.unshift(0)
|
||||||
|
let privateKeyDecimal = BigInteger(pk).toString()
|
||||||
|
let privateKeyHex = Crypto.util.bytesToHex(pk)
|
||||||
|
return {
|
||||||
|
privateKeyDecimal: privateKeyDecimal,
|
||||||
|
privateKeyHex: privateKeyHex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//generate a random Interger within range
|
||||||
|
floCrypto.randInt = function (min, max) {
|
||||||
|
min = Math.ceil(min);
|
||||||
|
max = Math.floor(max);
|
||||||
|
return Math.floor(securedMathRandom() * (max - min + 1)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
//generate a random String within length (options : alphaNumeric chars only)
|
||||||
|
floCrypto.randString = function (length, alphaNumeric = true) {
|
||||||
|
var result = '';
|
||||||
|
var characters = alphaNumeric ? 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' :
|
||||||
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+-./*?@#&$<>=[]{}():';
|
||||||
|
for (var i = 0; i < length; i++)
|
||||||
|
result += characters.charAt(Math.floor(securedMathRandom() * characters.length));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Encrypt Data using public-key
|
||||||
|
floCrypto.encryptData = function (data, receiverPublicKeyHex) {
|
||||||
|
var senderECKeyData = getSenderPublicKeyString();
|
||||||
|
var senderDerivedKey = deriveSharedKeySender(receiverPublicKeyHex, senderECKeyData.privateKey);
|
||||||
|
let senderKey = senderDerivedKey.XValue + senderDerivedKey.YValue;
|
||||||
|
let secret = Crypto.AES.encrypt(data, senderKey);
|
||||||
|
return {
|
||||||
|
secret: secret,
|
||||||
|
senderPublicKeyString: senderECKeyData.senderPublicKeyString
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//Decrypt Data using private-key
|
||||||
|
floCrypto.decryptData = function (data, privateKeyHex) {
|
||||||
|
var receiverECKeyData = {};
|
||||||
|
if (typeof privateKeyHex !== "string") throw new Error("No private key found.");
|
||||||
|
let privateKey = wifToDecimal(privateKeyHex, true);
|
||||||
|
if (typeof privateKey.privateKeyDecimal !== "string") throw new Error("Failed to detremine your private key.");
|
||||||
|
receiverECKeyData.privateKey = privateKey.privateKeyDecimal;
|
||||||
|
var receiverDerivedKey = deriveSharedKeyReceiver(data.senderPublicKeyString, receiverECKeyData.privateKey);
|
||||||
|
let receiverKey = receiverDerivedKey.XValue + receiverDerivedKey.YValue;
|
||||||
|
let decryptMsg = Crypto.AES.decrypt(data.secret, receiverKey);
|
||||||
|
return decryptMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sign data using private-key
|
||||||
|
floCrypto.signData = function (data, privateKeyHex) {
|
||||||
|
var key = new Bitcoin.ECKey(privateKeyHex);
|
||||||
|
var messageHash = Crypto.SHA256(data);
|
||||||
|
var messageSign = Bitcoin.ECDSA.sign(messageHash, key.priv);
|
||||||
|
var sighex = Crypto.util.bytesToHex(messageSign);
|
||||||
|
return sighex;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Verify signatue of the data using public-key
|
||||||
|
floCrypto.verifySign = function (data, signatureHex, publicKeyHex) {
|
||||||
|
var msgHash = Crypto.SHA256(data);
|
||||||
|
var sigBytes = Crypto.util.hexToBytes(signatureHex);
|
||||||
|
var publicKeyPoint = ecparams.getCurve().decodePointHex(publicKeyHex);
|
||||||
|
var verify = Bitcoin.ECDSA.verify(msgHash, sigBytes, publicKeyPoint);
|
||||||
|
return verify;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Generates a new flo ID and returns private-key, public-key and floID
|
||||||
|
const generateNewID = floCrypto.generateNewID = function () {
|
||||||
|
var key = new Bitcoin.ECKey(false);
|
||||||
|
key.setCompressed(true);
|
||||||
|
return {
|
||||||
|
floID: key.getBitcoinAddress(),
|
||||||
|
pubKey: key.getPubKeyHex(),
|
||||||
|
privKey: key.getBitcoinWalletImportFormat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperties(floCrypto, {
|
||||||
|
newID: {
|
||||||
|
get: () => generateNewID()
|
||||||
|
},
|
||||||
|
hashID: {
|
||||||
|
value: (str) => {
|
||||||
|
let bytes = ripemd160(Crypto.SHA256(str, { asBytes: true }), { asBytes: true });
|
||||||
|
bytes.unshift(bitjs.pub);
|
||||||
|
var hash = Crypto.SHA256(Crypto.SHA256(bytes, {
|
||||||
|
asBytes: true
|
||||||
|
}), {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
var checksum = hash.slice(0, 4);
|
||||||
|
return bitjs.Base58.encode(bytes.concat(checksum));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tmpID: {
|
||||||
|
get: () => {
|
||||||
|
let bytes = Crypto.util.randomBytes(20);
|
||||||
|
bytes.unshift(bitjs.pub);
|
||||||
|
var hash = Crypto.SHA256(Crypto.SHA256(bytes, {
|
||||||
|
asBytes: true
|
||||||
|
}), {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
var checksum = hash.slice(0, 4);
|
||||||
|
return bitjs.Base58.encode(bytes.concat(checksum));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Returns public-key from private-key
|
||||||
|
floCrypto.getPubKeyHex = function (privateKeyHex) {
|
||||||
|
if (!privateKeyHex)
|
||||||
|
return null;
|
||||||
|
var key = new Bitcoin.ECKey(privateKeyHex);
|
||||||
|
if (key.priv == null)
|
||||||
|
return null;
|
||||||
|
key.setCompressed(true);
|
||||||
|
return key.getPubKeyHex();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Returns flo-ID from public-key or private-key
|
||||||
|
floCrypto.getFloID = function (keyHex) {
|
||||||
|
if (!keyHex)
|
||||||
|
return null;
|
||||||
|
try {
|
||||||
|
var key = new Bitcoin.ECKey(keyHex);
|
||||||
|
if (key.priv == null)
|
||||||
|
key.setPub(keyHex);
|
||||||
|
return key.getBitcoinAddress();
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
floCrypto.getAddress = function (privateKeyHex, strict = false) {
|
||||||
|
if (!privateKeyHex)
|
||||||
|
return;
|
||||||
|
var key = new Bitcoin.ECKey(privateKeyHex);
|
||||||
|
if (key.priv == null)
|
||||||
|
return null;
|
||||||
|
key.setCompressed(true);
|
||||||
|
let pubKey = key.getPubKeyHex(),
|
||||||
|
version = bitjs.Base58.decode(privateKeyHex)[0];
|
||||||
|
switch (version) {
|
||||||
|
case coinjs.priv: //BTC
|
||||||
|
return coinjs.bech32Address(pubKey).address;
|
||||||
|
case bitjs.priv: //FLO
|
||||||
|
return bitjs.pubkey2address(pubKey);
|
||||||
|
default:
|
||||||
|
return strict ? false : bitjs.pubkey2address(pubKey); //default to FLO address (if strict=false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Verify the private-key for the given public-key or flo-ID
|
||||||
|
floCrypto.verifyPrivKey = function (privateKeyHex, pubKey_floID, isfloID = true) {
|
||||||
|
if (!privateKeyHex || !pubKey_floID)
|
||||||
|
return false;
|
||||||
|
try {
|
||||||
|
var key = new Bitcoin.ECKey(privateKeyHex);
|
||||||
|
if (key.priv == null)
|
||||||
|
return false;
|
||||||
|
key.setCompressed(true);
|
||||||
|
if (isfloID && pubKey_floID == key.getBitcoinAddress())
|
||||||
|
return true;
|
||||||
|
else if (!isfloID && pubKey_floID.toUpperCase() == key.getPubKeyHex().toUpperCase())
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
floCrypto.getMultisigAddress = function (publicKeyList, requiredSignatures) {
|
||||||
|
if (!Array.isArray(publicKeyList) || !publicKeyList.length)
|
||||||
|
return null;
|
||||||
|
if (!Number.isInteger(requiredSignatures) || requiredSignatures < 1 || requiredSignatures > publicKeyList.length)
|
||||||
|
return null;
|
||||||
|
try {
|
||||||
|
var multisig = bitjs.pubkeys2multisig(publicKeyList, requiredSignatures);
|
||||||
|
return multisig;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
floCrypto.decodeRedeemScript = function (redeemScript) {
|
||||||
|
try {
|
||||||
|
var decoded = bitjs.transaction().decodeRedeemScript(redeemScript);
|
||||||
|
return decoded;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if the given flo-id is valid or not
|
||||||
|
floCrypto.validateFloID = function (floID, regularOnly = false) {
|
||||||
|
if (!floID)
|
||||||
|
return false;
|
||||||
|
try {
|
||||||
|
let addr = new Bitcoin.Address(floID);
|
||||||
|
if (regularOnly && addr.version != Bitcoin.Address.standardVersion)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if the given address (any blockchain) is valid or not
|
||||||
|
floCrypto.validateAddr = function (address, std = true, bech = true) {
|
||||||
|
let raw = decodeAddress(address);
|
||||||
|
if (!raw)
|
||||||
|
return false;
|
||||||
|
if (typeof raw.version !== 'undefined') { //legacy or segwit
|
||||||
|
if (std == false)
|
||||||
|
return false;
|
||||||
|
else if (std === true || (!Array.isArray(std) && std === raw.version) || (Array.isArray(std) && std.includes(raw.version)))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
} else if (typeof raw.bech_version !== 'undefined') { //bech32
|
||||||
|
if (bech === false)
|
||||||
|
return false;
|
||||||
|
else if (bech === true || (!Array.isArray(bech) && bech === raw.bech_version) || (Array.isArray(bech) && bech.includes(raw.bech_version)))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
} else //unknown
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check the public-key (or redeem-script) for the address (any blockchain)
|
||||||
|
floCrypto.verifyPubKey = function (pubKeyHex, address) {
|
||||||
|
let raw = decodeAddress(address);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
let pub_hash = Crypto.util.bytesToHex(ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubKeyHex), { asBytes: true })));
|
||||||
|
if (typeof raw.bech_version !== 'undefined' && raw.bytes.length == 32) //bech32-multisig
|
||||||
|
raw.hex = Crypto.util.bytesToHex(ripemd160(raw.bytes, { asBytes: true }));
|
||||||
|
return pub_hash === raw.hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Convert the given address (any blockchain) to equivalent floID
|
||||||
|
floCrypto.toFloID = function (address, options = null) {
|
||||||
|
if (!address)
|
||||||
|
return;
|
||||||
|
let raw = decodeAddress(address);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
else if (options) { //if (optional) version check is passed
|
||||||
|
if (typeof raw.version !== 'undefined' && (!options.std || !options.std.includes(raw.version)))
|
||||||
|
return;
|
||||||
|
if (typeof raw.bech_version !== 'undefined' && (!options.bech || !options.bech.includes(raw.bech_version)))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
raw.bytes.unshift(bitjs.pub);
|
||||||
|
let hash = Crypto.SHA256(Crypto.SHA256(raw.bytes, {
|
||||||
|
asBytes: true
|
||||||
|
}), {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Convert raw address bytes to floID
|
||||||
|
floCrypto.rawToFloID = function (raw_bytes) {
|
||||||
|
if (typeof raw_bytes === 'string')
|
||||||
|
raw_bytes = Crypto.util.hexToBytes(raw_bytes);
|
||||||
|
if (raw_bytes.length != 20)
|
||||||
|
return null;
|
||||||
|
raw_bytes.unshift(bitjs.pub);
|
||||||
|
let hash = Crypto.SHA256(Crypto.SHA256(raw_bytes, {
|
||||||
|
asBytes: true
|
||||||
|
}), {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
return bitjs.Base58.encode(raw_bytes.concat(hash.slice(0, 4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Convert the given multisig address (any blockchain) to equivalent multisig floID
|
||||||
|
floCrypto.toMultisigFloID = function (address, options = null) {
|
||||||
|
if (!address)
|
||||||
|
return;
|
||||||
|
let raw = decodeAddress(address);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
else if (options) { //if (optional) version check is passed
|
||||||
|
if (typeof raw.version !== 'undefined' && (!options.std || !options.std.includes(raw.version)))
|
||||||
|
return;
|
||||||
|
if (typeof raw.bech_version !== 'undefined' && (!options.bech || !options.bech.includes(raw.bech_version)))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof raw.bech_version !== 'undefined') {
|
||||||
|
if (raw.bytes.length != 32) return; //multisig bech address have 32 bytes
|
||||||
|
//multisig-bech:hash=SHA256 whereas multisig:hash=r160(SHA265), thus ripemd160 the bytes from multisig-bech
|
||||||
|
raw.bytes = ripemd160(raw.bytes, {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
raw.bytes.unshift(bitjs.multisig);
|
||||||
|
let hash = Crypto.SHA256(Crypto.SHA256(raw.bytes, {
|
||||||
|
asBytes: true
|
||||||
|
}), {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Checks if the given addresses (any blockchain) are same (w.r.t keys)
|
||||||
|
floCrypto.isSameAddr = function (addr1, addr2) {
|
||||||
|
if (!addr1 || !addr2)
|
||||||
|
return;
|
||||||
|
let raw1 = decodeAddress(addr1),
|
||||||
|
raw2 = decodeAddress(addr2);
|
||||||
|
if (!raw1 || !raw2)
|
||||||
|
return false;
|
||||||
|
else {
|
||||||
|
if (typeof raw1.bech_version !== 'undefined' && raw1.bytes.length == 32) //bech32-multisig
|
||||||
|
raw1.hex = Crypto.util.bytesToHex(ripemd160(raw1.bytes, { asBytes: true }));
|
||||||
|
if (typeof raw2.bech_version !== 'undefined' && raw2.bytes.length == 32) //bech32-multisig
|
||||||
|
raw2.hex = Crypto.util.bytesToHex(ripemd160(raw2.bytes, { asBytes: true }));
|
||||||
|
return raw1.hex === raw2.hex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const decodeAddress = floCrypto.decodeAddr = function (address) {
|
||||||
|
if (!address)
|
||||||
|
return;
|
||||||
|
else if (address.length == 33 || address.length == 34) { //legacy encoding
|
||||||
|
let decode = bitjs.Base58.decode(address);
|
||||||
|
let bytes = decode.slice(0, decode.length - 4);
|
||||||
|
let checksum = decode.slice(decode.length - 4),
|
||||||
|
hash = Crypto.SHA256(Crypto.SHA256(bytes, {
|
||||||
|
asBytes: true
|
||||||
|
}), {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
return (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3]) ? null : {
|
||||||
|
version: bytes.shift(),
|
||||||
|
hex: Crypto.util.bytesToHex(bytes),
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
} else if (address.length == 42 || address.length == 62) { //bech encoding
|
||||||
|
let decode = coinjs.bech32_decode(address);
|
||||||
|
if (decode) {
|
||||||
|
let bytes = decode.data;
|
||||||
|
let bech_version = bytes.shift();
|
||||||
|
bytes = coinjs.bech32_convert(bytes, 5, 8, false);
|
||||||
|
return {
|
||||||
|
bech_version,
|
||||||
|
hrp: decode.hrp,
|
||||||
|
hex: Crypto.util.bytesToHex(bytes),
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Split the str using shamir's Secret and Returns the shares
|
||||||
|
floCrypto.createShamirsSecretShares = function (str, total_shares, threshold_limit) {
|
||||||
|
try {
|
||||||
|
if (str.length > 0) {
|
||||||
|
var strHex = shamirSecretShare.str2hex(str);
|
||||||
|
var shares = shamirSecretShare.share(strHex, total_shares, threshold_limit);
|
||||||
|
return shares;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Returns the retrived secret by combining the shamirs shares
|
||||||
|
const retrieveShamirSecret = floCrypto.retrieveShamirSecret = function (sharesArray) {
|
||||||
|
try {
|
||||||
|
if (sharesArray.length > 0) {
|
||||||
|
var comb = shamirSecretShare.combine(sharesArray.slice(0, sharesArray.length));
|
||||||
|
comb = shamirSecretShare.hex2str(comb);
|
||||||
|
return comb;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Verifies the shares and str
|
||||||
|
floCrypto.verifyShamirsSecret = function (sharesArray, str) {
|
||||||
|
if (!str)
|
||||||
|
return null;
|
||||||
|
else if (retrieveShamirSecret(sharesArray) === str)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const validateASCII = floCrypto.validateASCII = function (string, bool = true) {
|
||||||
|
if (typeof string !== "string")
|
||||||
|
return null;
|
||||||
|
if (bool) {
|
||||||
|
let x;
|
||||||
|
for (let i = 0; i < string.length; i++) {
|
||||||
|
x = string.charCodeAt(i);
|
||||||
|
if (x < 32 || x > 127)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
let x, invalids = {};
|
||||||
|
for (let i = 0; i < string.length; i++) {
|
||||||
|
x = string.charCodeAt(i);
|
||||||
|
if (x < 32 || x > 127)
|
||||||
|
if (x in invalids)
|
||||||
|
invalids[string[i]].push(i)
|
||||||
|
else
|
||||||
|
invalids[string[i]] = [i];
|
||||||
|
}
|
||||||
|
if (Object.keys(invalids).length)
|
||||||
|
return invalids;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
floCrypto.convertToASCII = function (string, mode = 'soft-remove') {
|
||||||
|
let chars = validateASCII(string, false);
|
||||||
|
if (chars === true)
|
||||||
|
return string;
|
||||||
|
else if (chars === null)
|
||||||
|
return null;
|
||||||
|
let convertor, result = string,
|
||||||
|
refAlt = {};
|
||||||
|
ascii_alternatives.split('\n').forEach(a => refAlt[a[0]] = a.slice(2));
|
||||||
|
mode = mode.toLowerCase();
|
||||||
|
if (mode === "hard-unicode")
|
||||||
|
convertor = (c) => `\\u${('000' + c.charCodeAt().toString(16)).slice(-4)}`;
|
||||||
|
else if (mode === "soft-unicode")
|
||||||
|
convertor = (c) => refAlt[c] || `\\u${('000' + c.charCodeAt().toString(16)).slice(-4)}`;
|
||||||
|
else if (mode === "hard-remove")
|
||||||
|
convertor = c => "";
|
||||||
|
else if (mode === "soft-remove")
|
||||||
|
convertor = c => refAlt[c] || "";
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
for (let c in chars)
|
||||||
|
result = result.replaceAll(c, convertor(c));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
floCrypto.revertUnicode = function (string) {
|
||||||
|
return string.replace(/\\u[\dA-F]{4}/gi,
|
||||||
|
m => String.fromCharCode(parseInt(m.replace(/\\u/g, ''), 16)));
|
||||||
|
}
|
||||||
|
|
||||||
|
})('object' === typeof module ? module.exports : window.floCrypto = {});
|
||||||
57
scripts/floEthereum.js
Normal file
57
scripts/floEthereum.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
(function (EXPORTS) { //floEthereum v1.0.1a
|
||||||
|
/* FLO Ethereum Operators */
|
||||||
|
/* Make sure you added Taproot, Keccak, FLO and BTC Libraries before */
|
||||||
|
'use strict';
|
||||||
|
const floEthereum = EXPORTS;
|
||||||
|
|
||||||
|
const ethAddressFromPrivateKey = floEthereum.ethAddressFromPrivateKey = function (privateKey, onlyEvenY = false) {
|
||||||
|
var t1, t1_x, t1_y, t1_y_BigInt, t2, t3, t4;
|
||||||
|
var groupOrder = BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");
|
||||||
|
|
||||||
|
t1 = bitjs.newPubkey(privateKey);
|
||||||
|
t1_x = t1.slice(2, 66); t1_y = t1.slice(-64);
|
||||||
|
if (onlyEvenY) {
|
||||||
|
t1_y_BigInt = BigInt("0x" + t1_y);
|
||||||
|
if (t1_y_BigInt % 2n !== 0n) { t1_y_BigInt = (groupOrder - t1_y_BigInt) % groupOrder; t1_y = t1_y_BigInt.toString(16) }
|
||||||
|
};
|
||||||
|
|
||||||
|
t2 = t1_x.toString(16) + t1_y.toString(16);
|
||||||
|
t3 = keccak.keccak_256(Crypto.util.hexToBytes(t2));
|
||||||
|
t4 = keccak.extractLast20Bytes(t3);
|
||||||
|
return "0x" + t4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ethAddressFromCompressedPublicKey = floEthereum.ethAddressFromCompressedPublicKey = function (compressedPublicKey) {
|
||||||
|
var t1, t2, t3, t4;
|
||||||
|
t1 = coinjs.compressedToUncompressed(compressedPublicKey);
|
||||||
|
t2 = t1.slice(2);
|
||||||
|
t3 = keccak.keccak_256(Crypto.util.hexToBytes(t2));
|
||||||
|
t4 = keccak.extractLast20Bytes(t3);
|
||||||
|
return "0x" + t4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ethPrivateKeyFromUntweakedPrivateKey = floEthereum.ethPrivateKeyFromUntweakedPrivateKey = function (untweakedPrivateKey) {
|
||||||
|
var t1;
|
||||||
|
t1 = hex.encode(taproot.taprootTweakPrivKey(hex.decode(untweakedPrivateKey)));
|
||||||
|
return t1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ethAddressFromUntweakedPrivateKey = floEthereum.ethAddressFromUntweakedPrivateKey = function (untweakedPrivateKey) {
|
||||||
|
var t1, t2;
|
||||||
|
t1 = hex.encode(taproot.taprootTweakPrivKey(hex.decode(untweakedPrivateKey)));
|
||||||
|
t2 = ethAddressFromPrivateKey(t1);
|
||||||
|
return t2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ethAddressFromTaprootAddress = floEthereum.ethAddressFromTaprootAddress = function (taprootAddress) {
|
||||||
|
var t1, t2, t3, t4;
|
||||||
|
t1 = coinjs.addressDecode(taprootAddress);
|
||||||
|
t2 = t1.outstring.slice(4);
|
||||||
|
t3 = "02" + t2;
|
||||||
|
t4 = ethAddressFromCompressedPublicKey(t3);
|
||||||
|
return t4;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
})('object' === typeof module ? module.exports : window.floEthereum = {});
|
||||||
1
scripts/floEthereum.min.js
vendored
Normal file
1
scripts/floEthereum.min.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
!function(EXPORTS){"use strict";const floEthereum="object"===typeof module?module.exports:window.floEthereum={},ethAddressFromPrivateKey=floEthereum.ethAddressFromPrivateKey=function(privateKey,onlyEvenY=!1){var t1,t1_x,t1_y,t1_y_BigInt,t2,t3,groupOrder=BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");return t1_x=(t1=bitjs.newPubkey(privateKey)).slice(2,66),t1_y=t1.slice(-64),onlyEvenY&&(t1_y_BigInt=BigInt("0x"+t1_y))%2n!==0n&&(t1_y=(t1_y_BigInt=(groupOrder-t1_y_BigInt)%groupOrder).toString(16)),t2=t1_x.toString(16)+t1_y.toString(16),t3=keccak.keccak_256(Crypto.util.hexToBytes(t2)),"0x"+keccak.extractLast20Bytes(t3)},ethAddressFromCompressedPublicKey=floEthereum.ethAddressFromCompressedPublicKey=function(compressedPublicKey){var t2,t3;return t2=coinjs.compressedToUncompressed(compressedPublicKey).slice(2),t3=keccak.keccak_256(Crypto.util.hexToBytes(t2)),"0x"+keccak.extractLast20Bytes(t3)};floEthereum.ethPrivateKeyFromUntweakedPrivateKey=function(untweakedPrivateKey){return hex.encode(taproot.taprootTweakPrivKey(hex.decode(untweakedPrivateKey)))},floEthereum.ethAddressFromUntweakedPrivateKey=function(untweakedPrivateKey){var t1;return t1=hex.encode(taproot.taprootTweakPrivKey(hex.decode(untweakedPrivateKey))),ethAddressFromPrivateKey(t1)},floEthereum.ethAddressFromTaprootAddress=function(taprootAddress){var t2;return t2=coinjs.addressDecode(taprootAddress).outstring.slice(4),ethAddressFromCompressedPublicKey("02"+t2)}}();
|
||||||
674
scripts/keccak.js
Normal file
674
scripts/keccak.js
Normal file
@ -0,0 +1,674 @@
|
|||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var INPUT_ERROR = 'input is invalid type';
|
||||||
|
var FINALIZE_ERROR = 'finalize already called';
|
||||||
|
var WINDOW = typeof window === 'object';
|
||||||
|
var root = WINDOW ? (window.keccak = window.keccak || {}) : {};
|
||||||
|
if (root.JS_SHA3_NO_WINDOW) {
|
||||||
|
WINDOW = false;
|
||||||
|
}
|
||||||
|
var WEB_WORKER = !WINDOW && typeof self === 'object';
|
||||||
|
var NODE_JS = !root.JS_SHA3_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
|
||||||
|
if (NODE_JS) {
|
||||||
|
root = global;
|
||||||
|
} else if (WEB_WORKER) {
|
||||||
|
root = self;
|
||||||
|
}
|
||||||
|
var COMMON_JS = !root.JS_SHA3_NO_COMMON_JS && typeof module === 'object' && module.exports;
|
||||||
|
var AMD = typeof define === 'function' && define.amd;
|
||||||
|
var ARRAY_BUFFER = !root.JS_SHA3_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
|
||||||
|
var HEX_CHARS = '0123456789abcdef'.split('');
|
||||||
|
var SHAKE_PADDING = [31, 7936, 2031616, 520093696];
|
||||||
|
var CSHAKE_PADDING = [4, 1024, 262144, 67108864];
|
||||||
|
var KECCAK_PADDING = [1, 256, 65536, 16777216];
|
||||||
|
var PADDING = [6, 1536, 393216, 100663296];
|
||||||
|
var SHIFT = [0, 8, 16, 24];
|
||||||
|
var RC = [1, 0, 32898, 0, 32906, 2147483648, 2147516416, 2147483648, 32907, 0, 2147483649,
|
||||||
|
0, 2147516545, 2147483648, 32777, 2147483648, 138, 0, 136, 0, 2147516425, 0,
|
||||||
|
2147483658, 0, 2147516555, 0, 139, 2147483648, 32905, 2147483648, 32771,
|
||||||
|
2147483648, 32770, 2147483648, 128, 2147483648, 32778, 0, 2147483658, 2147483648,
|
||||||
|
2147516545, 2147483648, 32896, 2147483648, 2147483649, 0, 2147516424, 2147483648];
|
||||||
|
var BITS = [224, 256, 384, 512];
|
||||||
|
var SHAKE_BITS = [128, 256];
|
||||||
|
var OUTPUT_TYPES = ['hex', 'buffer', 'arrayBuffer', 'array', 'digest'];
|
||||||
|
var CSHAKE_BYTEPAD = {
|
||||||
|
'128': 168,
|
||||||
|
'256': 136
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var isArray = root.JS_SHA3_NO_NODE_JS || !Array.isArray
|
||||||
|
? function (obj) {
|
||||||
|
return Object.prototype.toString.call(obj) === '[object Array]';
|
||||||
|
}
|
||||||
|
: Array.isArray;
|
||||||
|
|
||||||
|
var isView = (ARRAY_BUFFER && (root.JS_SHA3_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView))
|
||||||
|
? function (obj) {
|
||||||
|
return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
|
||||||
|
}
|
||||||
|
: ArrayBuffer.isView;
|
||||||
|
|
||||||
|
// [message: string, isString: bool]
|
||||||
|
var formatMessage = function (message) {
|
||||||
|
var type = typeof message;
|
||||||
|
if (type === 'string') {
|
||||||
|
return [message, true];
|
||||||
|
}
|
||||||
|
if (type !== 'object' || message === null) {
|
||||||
|
throw new Error(INPUT_ERROR);
|
||||||
|
}
|
||||||
|
if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
|
||||||
|
return [new Uint8Array(message), false];
|
||||||
|
}
|
||||||
|
if (!isArray(message) && !isView(message)) {
|
||||||
|
throw new Error(INPUT_ERROR);
|
||||||
|
}
|
||||||
|
return [message, false];
|
||||||
|
}
|
||||||
|
|
||||||
|
var empty = function (message) {
|
||||||
|
return formatMessage(message)[0].length === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
var createOutputMethod = function (bits, padding, outputType) {
|
||||||
|
return function (message) {
|
||||||
|
return new Keccak(bits, padding, bits).update(message)[outputType]();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var createShakeOutputMethod = function (bits, padding, outputType) {
|
||||||
|
return function (message, outputBits) {
|
||||||
|
return new Keccak(bits, padding, outputBits).update(message)[outputType]();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var createCshakeOutputMethod = function (bits, padding, outputType) {
|
||||||
|
return function (message, outputBits, n, s) {
|
||||||
|
return methods['cshake' + bits].update(message, outputBits, n, s)[outputType]();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var createKmacOutputMethod = function (bits, padding, outputType) {
|
||||||
|
return function (key, message, outputBits, s) {
|
||||||
|
return methods['kmac' + bits].update(key, message, outputBits, s)[outputType]();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var createOutputMethods = function (method, createMethod, bits, padding) {
|
||||||
|
for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
|
||||||
|
var type = OUTPUT_TYPES[i];
|
||||||
|
method[type] = createMethod(bits, padding, type);
|
||||||
|
}
|
||||||
|
return method;
|
||||||
|
};
|
||||||
|
|
||||||
|
var createMethod = function (bits, padding) {
|
||||||
|
var method = createOutputMethod(bits, padding, 'hex');
|
||||||
|
method.create = function () {
|
||||||
|
return new Keccak(bits, padding, bits);
|
||||||
|
};
|
||||||
|
method.update = function (message) {
|
||||||
|
return method.create().update(message);
|
||||||
|
};
|
||||||
|
return createOutputMethods(method, createOutputMethod, bits, padding);
|
||||||
|
};
|
||||||
|
|
||||||
|
var createShakeMethod = function (bits, padding) {
|
||||||
|
var method = createShakeOutputMethod(bits, padding, 'hex');
|
||||||
|
method.create = function (outputBits) {
|
||||||
|
return new Keccak(bits, padding, outputBits);
|
||||||
|
};
|
||||||
|
method.update = function (message, outputBits) {
|
||||||
|
return method.create(outputBits).update(message);
|
||||||
|
};
|
||||||
|
return createOutputMethods(method, createShakeOutputMethod, bits, padding);
|
||||||
|
};
|
||||||
|
|
||||||
|
var createCshakeMethod = function (bits, padding) {
|
||||||
|
var w = CSHAKE_BYTEPAD[bits];
|
||||||
|
var method = createCshakeOutputMethod(bits, padding, 'hex');
|
||||||
|
method.create = function (outputBits, n, s) {
|
||||||
|
if (empty(n) && empty(s)) {
|
||||||
|
return methods['shake' + bits].create(outputBits);
|
||||||
|
} else {
|
||||||
|
return new Keccak(bits, padding, outputBits).bytepad([n, s], w);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
method.update = function (message, outputBits, n, s) {
|
||||||
|
return method.create(outputBits, n, s).update(message);
|
||||||
|
};
|
||||||
|
return createOutputMethods(method, createCshakeOutputMethod, bits, padding);
|
||||||
|
};
|
||||||
|
|
||||||
|
var createKmacMethod = function (bits, padding) {
|
||||||
|
var w = CSHAKE_BYTEPAD[bits];
|
||||||
|
var method = createKmacOutputMethod(bits, padding, 'hex');
|
||||||
|
method.create = function (key, outputBits, s) {
|
||||||
|
return new Kmac(bits, padding, outputBits).bytepad(['KMAC', s], w).bytepad([key], w);
|
||||||
|
};
|
||||||
|
method.update = function (key, message, outputBits, s) {
|
||||||
|
return method.create(key, outputBits, s).update(message);
|
||||||
|
};
|
||||||
|
return createOutputMethods(method, createKmacOutputMethod, bits, padding);
|
||||||
|
};
|
||||||
|
|
||||||
|
var algorithms = [
|
||||||
|
{ name: 'keccak', padding: KECCAK_PADDING, bits: BITS, createMethod: createMethod },
|
||||||
|
{ name: 'sha3', padding: PADDING, bits: BITS, createMethod: createMethod },
|
||||||
|
{ name: 'shake', padding: SHAKE_PADDING, bits: SHAKE_BITS, createMethod: createShakeMethod },
|
||||||
|
{ name: 'cshake', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createCshakeMethod },
|
||||||
|
{ name: 'kmac', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createKmacMethod }
|
||||||
|
];
|
||||||
|
|
||||||
|
var methods = {}, methodNames = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < algorithms.length; ++i) {
|
||||||
|
var algorithm = algorithms[i];
|
||||||
|
var bits = algorithm.bits;
|
||||||
|
for (var j = 0; j < bits.length; ++j) {
|
||||||
|
var methodName = algorithm.name + '_' + bits[j];
|
||||||
|
methodNames.push(methodName);
|
||||||
|
methods[methodName] = algorithm.createMethod(bits[j], algorithm.padding);
|
||||||
|
if (algorithm.name !== 'sha3') {
|
||||||
|
var newMethodName = algorithm.name + bits[j];
|
||||||
|
methodNames.push(newMethodName);
|
||||||
|
methods[newMethodName] = methods[methodName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
methodNames.push("extractLast20Bytes");
|
||||||
|
methods["extractLast20Bytes"] = extractLast20Bytes;
|
||||||
|
|
||||||
|
|
||||||
|
function Keccak(bits, padding, outputBits) {
|
||||||
|
this.blocks = [];
|
||||||
|
this.s = [];
|
||||||
|
this.padding = padding;
|
||||||
|
this.outputBits = outputBits;
|
||||||
|
this.reset = true;
|
||||||
|
this.finalized = false;
|
||||||
|
this.block = 0;
|
||||||
|
this.start = 0;
|
||||||
|
this.blockCount = (1600 - (bits << 1)) >> 5;
|
||||||
|
this.byteCount = this.blockCount << 2;
|
||||||
|
this.outputBlocks = outputBits >> 5;
|
||||||
|
this.extraBytes = (outputBits & 31) >> 3;
|
||||||
|
|
||||||
|
for (var i = 0; i < 50; ++i) {
|
||||||
|
this.s[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keccak.prototype.update = function (message) {
|
||||||
|
if (this.finalized) {
|
||||||
|
throw new Error(FINALIZE_ERROR);
|
||||||
|
}
|
||||||
|
var result = formatMessage(message);
|
||||||
|
message = result[0];
|
||||||
|
var isString = result[1];
|
||||||
|
var blocks = this.blocks, byteCount = this.byteCount, length = message.length,
|
||||||
|
blockCount = this.blockCount, index = 0, s = this.s, i, code;
|
||||||
|
|
||||||
|
while (index < length) {
|
||||||
|
if (this.reset) {
|
||||||
|
this.reset = false;
|
||||||
|
blocks[0] = this.block;
|
||||||
|
for (i = 1; i < blockCount + 1; ++i) {
|
||||||
|
blocks[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isString) {
|
||||||
|
for (i = this.start; index < length && i < byteCount; ++index) {
|
||||||
|
code = message.charCodeAt(index);
|
||||||
|
if (code < 0x80) {
|
||||||
|
blocks[i >> 2] |= code << SHIFT[i++ & 3];
|
||||||
|
} else if (code < 0x800) {
|
||||||
|
blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
|
||||||
|
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
||||||
|
} else if (code < 0xd800 || code >= 0xe000) {
|
||||||
|
blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
|
||||||
|
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
|
||||||
|
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
||||||
|
} else {
|
||||||
|
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
|
||||||
|
blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
|
||||||
|
blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
|
||||||
|
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
|
||||||
|
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i = this.start; index < length && i < byteCount; ++index) {
|
||||||
|
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.lastByteIndex = i;
|
||||||
|
if (i >= byteCount) {
|
||||||
|
this.start = i - byteCount;
|
||||||
|
this.block = blocks[blockCount];
|
||||||
|
for (i = 0; i < blockCount; ++i) {
|
||||||
|
s[i] ^= blocks[i];
|
||||||
|
}
|
||||||
|
f(s);
|
||||||
|
this.reset = true;
|
||||||
|
} else {
|
||||||
|
this.start = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Keccak.prototype.encode = function (x, right) {
|
||||||
|
var o = x & 255, n = 1;
|
||||||
|
var bytes = [o];
|
||||||
|
x = x >> 8;
|
||||||
|
o = x & 255;
|
||||||
|
while (o > 0) {
|
||||||
|
bytes.unshift(o);
|
||||||
|
x = x >> 8;
|
||||||
|
o = x & 255;
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
if (right) {
|
||||||
|
bytes.push(n);
|
||||||
|
} else {
|
||||||
|
bytes.unshift(n);
|
||||||
|
}
|
||||||
|
this.update(bytes);
|
||||||
|
return bytes.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
Keccak.prototype.encodeString = function (str) {
|
||||||
|
var result = formatMessage(str);
|
||||||
|
str = result[0];
|
||||||
|
var isString = result[1];
|
||||||
|
var bytes = 0, length = str.length;
|
||||||
|
if (isString) {
|
||||||
|
for (var i = 0; i < str.length; ++i) {
|
||||||
|
var code = str.charCodeAt(i);
|
||||||
|
if (code < 0x80) {
|
||||||
|
bytes += 1;
|
||||||
|
} else if (code < 0x800) {
|
||||||
|
bytes += 2;
|
||||||
|
} else if (code < 0xd800 || code >= 0xe000) {
|
||||||
|
bytes += 3;
|
||||||
|
} else {
|
||||||
|
code = 0x10000 + (((code & 0x3ff) << 10) | (str.charCodeAt(++i) & 0x3ff));
|
||||||
|
bytes += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bytes = length;
|
||||||
|
}
|
||||||
|
bytes += this.encode(bytes * 8);
|
||||||
|
this.update(str);
|
||||||
|
return bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
Keccak.prototype.bytepad = function (strs, w) {
|
||||||
|
var bytes = this.encode(w);
|
||||||
|
for (var i = 0; i < strs.length; ++i) {
|
||||||
|
bytes += this.encodeString(strs[i]);
|
||||||
|
}
|
||||||
|
var paddingBytes = (w - bytes % w) % w;
|
||||||
|
var zeros = [];
|
||||||
|
zeros.length = paddingBytes;
|
||||||
|
this.update(zeros);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Keccak.prototype.finalize = function () {
|
||||||
|
if (this.finalized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.finalized = true;
|
||||||
|
var blocks = this.blocks, i = this.lastByteIndex, blockCount = this.blockCount, s = this.s;
|
||||||
|
blocks[i >> 2] |= this.padding[i & 3];
|
||||||
|
if (this.lastByteIndex === this.byteCount) {
|
||||||
|
blocks[0] = blocks[blockCount];
|
||||||
|
for (i = 1; i < blockCount + 1; ++i) {
|
||||||
|
blocks[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blocks[blockCount - 1] |= 0x80000000;
|
||||||
|
for (i = 0; i < blockCount; ++i) {
|
||||||
|
s[i] ^= blocks[i];
|
||||||
|
}
|
||||||
|
f(s);
|
||||||
|
};
|
||||||
|
|
||||||
|
Keccak.prototype.toString = Keccak.prototype.hex = function () {
|
||||||
|
this.finalize();
|
||||||
|
|
||||||
|
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
|
||||||
|
extraBytes = this.extraBytes, i = 0, j = 0;
|
||||||
|
var hex = '', block;
|
||||||
|
while (j < outputBlocks) {
|
||||||
|
for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
|
||||||
|
block = s[i];
|
||||||
|
hex += HEX_CHARS[(block >> 4) & 0x0F] + HEX_CHARS[block & 0x0F] +
|
||||||
|
HEX_CHARS[(block >> 12) & 0x0F] + HEX_CHARS[(block >> 8) & 0x0F] +
|
||||||
|
HEX_CHARS[(block >> 20) & 0x0F] + HEX_CHARS[(block >> 16) & 0x0F] +
|
||||||
|
HEX_CHARS[(block >> 28) & 0x0F] + HEX_CHARS[(block >> 24) & 0x0F];
|
||||||
|
}
|
||||||
|
if (j % blockCount === 0) {
|
||||||
|
f(s);
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (extraBytes) {
|
||||||
|
block = s[i];
|
||||||
|
hex += HEX_CHARS[(block >> 4) & 0x0F] + HEX_CHARS[block & 0x0F];
|
||||||
|
if (extraBytes > 1) {
|
||||||
|
hex += HEX_CHARS[(block >> 12) & 0x0F] + HEX_CHARS[(block >> 8) & 0x0F];
|
||||||
|
}
|
||||||
|
if (extraBytes > 2) {
|
||||||
|
hex += HEX_CHARS[(block >> 20) & 0x0F] + HEX_CHARS[(block >> 16) & 0x0F];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hex;
|
||||||
|
};
|
||||||
|
|
||||||
|
Keccak.prototype.arrayBuffer = function () {
|
||||||
|
this.finalize();
|
||||||
|
|
||||||
|
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
|
||||||
|
extraBytes = this.extraBytes, i = 0, j = 0;
|
||||||
|
var bytes = this.outputBits >> 3;
|
||||||
|
var buffer;
|
||||||
|
if (extraBytes) {
|
||||||
|
buffer = new ArrayBuffer((outputBlocks + 1) << 2);
|
||||||
|
} else {
|
||||||
|
buffer = new ArrayBuffer(bytes);
|
||||||
|
}
|
||||||
|
var array = new Uint32Array(buffer);
|
||||||
|
while (j < outputBlocks) {
|
||||||
|
for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
|
||||||
|
array[j] = s[i];
|
||||||
|
}
|
||||||
|
if (j % blockCount === 0) {
|
||||||
|
f(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (extraBytes) {
|
||||||
|
array[i] = s[i];
|
||||||
|
buffer = buffer.slice(0, bytes);
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
Keccak.prototype.buffer = Keccak.prototype.arrayBuffer;
|
||||||
|
|
||||||
|
Keccak.prototype.digest = Keccak.prototype.array = function () {
|
||||||
|
this.finalize();
|
||||||
|
|
||||||
|
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
|
||||||
|
extraBytes = this.extraBytes, i = 0, j = 0;
|
||||||
|
var array = [], offset, block;
|
||||||
|
while (j < outputBlocks) {
|
||||||
|
for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
|
||||||
|
offset = j << 2;
|
||||||
|
block = s[i];
|
||||||
|
array[offset] = block & 0xFF;
|
||||||
|
array[offset + 1] = (block >> 8) & 0xFF;
|
||||||
|
array[offset + 2] = (block >> 16) & 0xFF;
|
||||||
|
array[offset + 3] = (block >> 24) & 0xFF;
|
||||||
|
}
|
||||||
|
if (j % blockCount === 0) {
|
||||||
|
f(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (extraBytes) {
|
||||||
|
offset = j << 2;
|
||||||
|
block = s[i];
|
||||||
|
array[offset] = block & 0xFF;
|
||||||
|
if (extraBytes > 1) {
|
||||||
|
array[offset + 1] = (block >> 8) & 0xFF;
|
||||||
|
}
|
||||||
|
if (extraBytes > 2) {
|
||||||
|
array[offset + 2] = (block >> 16) & 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
};
|
||||||
|
|
||||||
|
function Kmac(bits, padding, outputBits) {
|
||||||
|
Keccak.call(this, bits, padding, outputBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
Kmac.prototype = new Keccak();
|
||||||
|
|
||||||
|
Kmac.prototype.finalize = function () {
|
||||||
|
this.encode(this.outputBits, true);
|
||||||
|
return Keccak.prototype.finalize.call(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
var f = function (s) {
|
||||||
|
var h, l, n, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9,
|
||||||
|
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17,
|
||||||
|
b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33,
|
||||||
|
b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49;
|
||||||
|
for (n = 0; n < 48; n += 2) {
|
||||||
|
c0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40];
|
||||||
|
c1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41];
|
||||||
|
c2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42];
|
||||||
|
c3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43];
|
||||||
|
c4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44];
|
||||||
|
c5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45];
|
||||||
|
c6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46];
|
||||||
|
c7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47];
|
||||||
|
c8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48];
|
||||||
|
c9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49];
|
||||||
|
|
||||||
|
h = c8 ^ ((c2 << 1) | (c3 >>> 31));
|
||||||
|
l = c9 ^ ((c3 << 1) | (c2 >>> 31));
|
||||||
|
s[0] ^= h;
|
||||||
|
s[1] ^= l;
|
||||||
|
s[10] ^= h;
|
||||||
|
s[11] ^= l;
|
||||||
|
s[20] ^= h;
|
||||||
|
s[21] ^= l;
|
||||||
|
s[30] ^= h;
|
||||||
|
s[31] ^= l;
|
||||||
|
s[40] ^= h;
|
||||||
|
s[41] ^= l;
|
||||||
|
h = c0 ^ ((c4 << 1) | (c5 >>> 31));
|
||||||
|
l = c1 ^ ((c5 << 1) | (c4 >>> 31));
|
||||||
|
s[2] ^= h;
|
||||||
|
s[3] ^= l;
|
||||||
|
s[12] ^= h;
|
||||||
|
s[13] ^= l;
|
||||||
|
s[22] ^= h;
|
||||||
|
s[23] ^= l;
|
||||||
|
s[32] ^= h;
|
||||||
|
s[33] ^= l;
|
||||||
|
s[42] ^= h;
|
||||||
|
s[43] ^= l;
|
||||||
|
h = c2 ^ ((c6 << 1) | (c7 >>> 31));
|
||||||
|
l = c3 ^ ((c7 << 1) | (c6 >>> 31));
|
||||||
|
s[4] ^= h;
|
||||||
|
s[5] ^= l;
|
||||||
|
s[14] ^= h;
|
||||||
|
s[15] ^= l;
|
||||||
|
s[24] ^= h;
|
||||||
|
s[25] ^= l;
|
||||||
|
s[34] ^= h;
|
||||||
|
s[35] ^= l;
|
||||||
|
s[44] ^= h;
|
||||||
|
s[45] ^= l;
|
||||||
|
h = c4 ^ ((c8 << 1) | (c9 >>> 31));
|
||||||
|
l = c5 ^ ((c9 << 1) | (c8 >>> 31));
|
||||||
|
s[6] ^= h;
|
||||||
|
s[7] ^= l;
|
||||||
|
s[16] ^= h;
|
||||||
|
s[17] ^= l;
|
||||||
|
s[26] ^= h;
|
||||||
|
s[27] ^= l;
|
||||||
|
s[36] ^= h;
|
||||||
|
s[37] ^= l;
|
||||||
|
s[46] ^= h;
|
||||||
|
s[47] ^= l;
|
||||||
|
h = c6 ^ ((c0 << 1) | (c1 >>> 31));
|
||||||
|
l = c7 ^ ((c1 << 1) | (c0 >>> 31));
|
||||||
|
s[8] ^= h;
|
||||||
|
s[9] ^= l;
|
||||||
|
s[18] ^= h;
|
||||||
|
s[19] ^= l;
|
||||||
|
s[28] ^= h;
|
||||||
|
s[29] ^= l;
|
||||||
|
s[38] ^= h;
|
||||||
|
s[39] ^= l;
|
||||||
|
s[48] ^= h;
|
||||||
|
s[49] ^= l;
|
||||||
|
|
||||||
|
b0 = s[0];
|
||||||
|
b1 = s[1];
|
||||||
|
b32 = (s[11] << 4) | (s[10] >>> 28);
|
||||||
|
b33 = (s[10] << 4) | (s[11] >>> 28);
|
||||||
|
b14 = (s[20] << 3) | (s[21] >>> 29);
|
||||||
|
b15 = (s[21] << 3) | (s[20] >>> 29);
|
||||||
|
b46 = (s[31] << 9) | (s[30] >>> 23);
|
||||||
|
b47 = (s[30] << 9) | (s[31] >>> 23);
|
||||||
|
b28 = (s[40] << 18) | (s[41] >>> 14);
|
||||||
|
b29 = (s[41] << 18) | (s[40] >>> 14);
|
||||||
|
b20 = (s[2] << 1) | (s[3] >>> 31);
|
||||||
|
b21 = (s[3] << 1) | (s[2] >>> 31);
|
||||||
|
b2 = (s[13] << 12) | (s[12] >>> 20);
|
||||||
|
b3 = (s[12] << 12) | (s[13] >>> 20);
|
||||||
|
b34 = (s[22] << 10) | (s[23] >>> 22);
|
||||||
|
b35 = (s[23] << 10) | (s[22] >>> 22);
|
||||||
|
b16 = (s[33] << 13) | (s[32] >>> 19);
|
||||||
|
b17 = (s[32] << 13) | (s[33] >>> 19);
|
||||||
|
b48 = (s[42] << 2) | (s[43] >>> 30);
|
||||||
|
b49 = (s[43] << 2) | (s[42] >>> 30);
|
||||||
|
b40 = (s[5] << 30) | (s[4] >>> 2);
|
||||||
|
b41 = (s[4] << 30) | (s[5] >>> 2);
|
||||||
|
b22 = (s[14] << 6) | (s[15] >>> 26);
|
||||||
|
b23 = (s[15] << 6) | (s[14] >>> 26);
|
||||||
|
b4 = (s[25] << 11) | (s[24] >>> 21);
|
||||||
|
b5 = (s[24] << 11) | (s[25] >>> 21);
|
||||||
|
b36 = (s[34] << 15) | (s[35] >>> 17);
|
||||||
|
b37 = (s[35] << 15) | (s[34] >>> 17);
|
||||||
|
b18 = (s[45] << 29) | (s[44] >>> 3);
|
||||||
|
b19 = (s[44] << 29) | (s[45] >>> 3);
|
||||||
|
b10 = (s[6] << 28) | (s[7] >>> 4);
|
||||||
|
b11 = (s[7] << 28) | (s[6] >>> 4);
|
||||||
|
b42 = (s[17] << 23) | (s[16] >>> 9);
|
||||||
|
b43 = (s[16] << 23) | (s[17] >>> 9);
|
||||||
|
b24 = (s[26] << 25) | (s[27] >>> 7);
|
||||||
|
b25 = (s[27] << 25) | (s[26] >>> 7);
|
||||||
|
b6 = (s[36] << 21) | (s[37] >>> 11);
|
||||||
|
b7 = (s[37] << 21) | (s[36] >>> 11);
|
||||||
|
b38 = (s[47] << 24) | (s[46] >>> 8);
|
||||||
|
b39 = (s[46] << 24) | (s[47] >>> 8);
|
||||||
|
b30 = (s[8] << 27) | (s[9] >>> 5);
|
||||||
|
b31 = (s[9] << 27) | (s[8] >>> 5);
|
||||||
|
b12 = (s[18] << 20) | (s[19] >>> 12);
|
||||||
|
b13 = (s[19] << 20) | (s[18] >>> 12);
|
||||||
|
b44 = (s[29] << 7) | (s[28] >>> 25);
|
||||||
|
b45 = (s[28] << 7) | (s[29] >>> 25);
|
||||||
|
b26 = (s[38] << 8) | (s[39] >>> 24);
|
||||||
|
b27 = (s[39] << 8) | (s[38] >>> 24);
|
||||||
|
b8 = (s[48] << 14) | (s[49] >>> 18);
|
||||||
|
b9 = (s[49] << 14) | (s[48] >>> 18);
|
||||||
|
|
||||||
|
s[0] = b0 ^ (~b2 & b4);
|
||||||
|
s[1] = b1 ^ (~b3 & b5);
|
||||||
|
s[10] = b10 ^ (~b12 & b14);
|
||||||
|
s[11] = b11 ^ (~b13 & b15);
|
||||||
|
s[20] = b20 ^ (~b22 & b24);
|
||||||
|
s[21] = b21 ^ (~b23 & b25);
|
||||||
|
s[30] = b30 ^ (~b32 & b34);
|
||||||
|
s[31] = b31 ^ (~b33 & b35);
|
||||||
|
s[40] = b40 ^ (~b42 & b44);
|
||||||
|
s[41] = b41 ^ (~b43 & b45);
|
||||||
|
s[2] = b2 ^ (~b4 & b6);
|
||||||
|
s[3] = b3 ^ (~b5 & b7);
|
||||||
|
s[12] = b12 ^ (~b14 & b16);
|
||||||
|
s[13] = b13 ^ (~b15 & b17);
|
||||||
|
s[22] = b22 ^ (~b24 & b26);
|
||||||
|
s[23] = b23 ^ (~b25 & b27);
|
||||||
|
s[32] = b32 ^ (~b34 & b36);
|
||||||
|
s[33] = b33 ^ (~b35 & b37);
|
||||||
|
s[42] = b42 ^ (~b44 & b46);
|
||||||
|
s[43] = b43 ^ (~b45 & b47);
|
||||||
|
s[4] = b4 ^ (~b6 & b8);
|
||||||
|
s[5] = b5 ^ (~b7 & b9);
|
||||||
|
s[14] = b14 ^ (~b16 & b18);
|
||||||
|
s[15] = b15 ^ (~b17 & b19);
|
||||||
|
s[24] = b24 ^ (~b26 & b28);
|
||||||
|
s[25] = b25 ^ (~b27 & b29);
|
||||||
|
s[34] = b34 ^ (~b36 & b38);
|
||||||
|
s[35] = b35 ^ (~b37 & b39);
|
||||||
|
s[44] = b44 ^ (~b46 & b48);
|
||||||
|
s[45] = b45 ^ (~b47 & b49);
|
||||||
|
s[6] = b6 ^ (~b8 & b0);
|
||||||
|
s[7] = b7 ^ (~b9 & b1);
|
||||||
|
s[16] = b16 ^ (~b18 & b10);
|
||||||
|
s[17] = b17 ^ (~b19 & b11);
|
||||||
|
s[26] = b26 ^ (~b28 & b20);
|
||||||
|
s[27] = b27 ^ (~b29 & b21);
|
||||||
|
s[36] = b36 ^ (~b38 & b30);
|
||||||
|
s[37] = b37 ^ (~b39 & b31);
|
||||||
|
s[46] = b46 ^ (~b48 & b40);
|
||||||
|
s[47] = b47 ^ (~b49 & b41);
|
||||||
|
s[8] = b8 ^ (~b0 & b2);
|
||||||
|
s[9] = b9 ^ (~b1 & b3);
|
||||||
|
s[18] = b18 ^ (~b10 & b12);
|
||||||
|
s[19] = b19 ^ (~b11 & b13);
|
||||||
|
s[28] = b28 ^ (~b20 & b22);
|
||||||
|
s[29] = b29 ^ (~b21 & b23);
|
||||||
|
s[38] = b38 ^ (~b30 & b32);
|
||||||
|
s[39] = b39 ^ (~b31 & b33);
|
||||||
|
s[48] = b48 ^ (~b40 & b42);
|
||||||
|
s[49] = b49 ^ (~b41 & b43);
|
||||||
|
|
||||||
|
s[0] ^= RC[n];
|
||||||
|
s[1] ^= RC[n + 1];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function extractLast20Bytes(hexString, addPrefix) {
|
||||||
|
// Ensure the input hexString has '0x' prefix
|
||||||
|
if (!hexString.startsWith('0x')) {
|
||||||
|
hexString = '0x' + hexString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove '0x' prefix and parse the hex string to a BigInt
|
||||||
|
var bigIntValue = BigInt(hexString);
|
||||||
|
|
||||||
|
// Extract the last 20 bytes (160 bits) from the BigInt
|
||||||
|
var last20Bytes = bigIntValue & BigInt('0x' + 'f'.repeat(40)); // 0xf is 4 bits in hexadecimal, repeated 40 times for 160 bits
|
||||||
|
|
||||||
|
// Convert the result back to a hexadecimal string
|
||||||
|
var result = last20Bytes.toString(16).padStart(40, '0'); // 40 characters for 160 bits
|
||||||
|
|
||||||
|
// Add '0x' prefix if addPrefix is truthy
|
||||||
|
if (addPrefix) {
|
||||||
|
result = '0x' + result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof root.keccak === 'object') {
|
||||||
|
Object.assign(root.keccak, methods);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (COMMON_JS) {
|
||||||
|
module.exports = methods;
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < methodNames.length; ++i) {
|
||||||
|
root[methodNames[i]] = methods[methodNames[i]];
|
||||||
|
}
|
||||||
|
if (AMD) {
|
||||||
|
define(function () {
|
||||||
|
return methods;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
8428
scripts/tap_combined.js
Normal file
8428
scripts/tap_combined.js
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user