avax wallet setup
This commit is contained in:
commit
f81fa7220e
209
avaxBlockchainAPI.js
Normal file
209
avaxBlockchainAPI.js
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
const RPC_URL =
|
||||||
|
"https://go.getblock.io/2e411fe60c824a94951fddbe9e7922ea/ext/bc/C/rpc";
|
||||||
|
|
||||||
|
function weiToAvax(weiAmount) {
|
||||||
|
try {
|
||||||
|
if (!weiAmount || weiAmount === "0" || weiAmount === 0) {
|
||||||
|
return "0.000000";
|
||||||
|
}
|
||||||
|
const weiString = weiAmount.toString();
|
||||||
|
|
||||||
|
let wei;
|
||||||
|
if (weiString.startsWith("0x")) {
|
||||||
|
// Hexadecimal input
|
||||||
|
wei = BigInteger(weiString.substring(2), 16);
|
||||||
|
} else {
|
||||||
|
// Decimal input
|
||||||
|
wei = BigInteger(weiString, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
const avaxDecimals = Math.pow(10, 18);
|
||||||
|
const result = (parseFloat(wei.toString()) / avaxDecimals).toFixed(6);
|
||||||
|
|
||||||
|
console.log("Wei conversion:", weiAmount, "->", result, "AVAX");
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error converting Wei to AVAX:", error, "Input:", weiAmount);
|
||||||
|
return "0.000000";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch balance via RPC
|
||||||
|
async function getBalanceRPC(address) {
|
||||||
|
try {
|
||||||
|
const body = {
|
||||||
|
jsonrpc: "2.0",
|
||||||
|
id: 1,
|
||||||
|
method: "eth_getBalance",
|
||||||
|
params: [address, "latest"],
|
||||||
|
};
|
||||||
|
const resp = await fetch(RPC_URL, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
});
|
||||||
|
const j = await resp.json();
|
||||||
|
|
||||||
|
if (j.error) {
|
||||||
|
throw new Error(j.error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
avax: weiToAvax(j.result),
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching balance:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get transaction count (nonce)
|
||||||
|
async function getTransactionCount(address) {
|
||||||
|
try {
|
||||||
|
const body = {
|
||||||
|
jsonrpc: "2.0",
|
||||||
|
id: 1,
|
||||||
|
method: "eth_getTransactionCount",
|
||||||
|
params: [address, "latest"],
|
||||||
|
};
|
||||||
|
const resp = await fetch(RPC_URL, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
});
|
||||||
|
const j = await resp.json();
|
||||||
|
|
||||||
|
if (j.error) {
|
||||||
|
throw new Error(j.error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseInt(j.result, 16);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching nonce:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current gas price
|
||||||
|
async function getGasPrice() {
|
||||||
|
try {
|
||||||
|
const body = {
|
||||||
|
jsonrpc: "2.0",
|
||||||
|
id: 1,
|
||||||
|
method: "eth_gasPrice",
|
||||||
|
params: [],
|
||||||
|
};
|
||||||
|
const resp = await fetch(RPC_URL, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
});
|
||||||
|
const j = await resp.json();
|
||||||
|
|
||||||
|
if (j.error) {
|
||||||
|
throw new Error(j.error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return j.result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching gas price:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare transaction data for Avalanche C-Chain
|
||||||
|
async function prepareAvalancheTransaction(
|
||||||
|
privateKey,
|
||||||
|
recipientAddress,
|
||||||
|
amountInAvax
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
// Validate inputs
|
||||||
|
if (!privateKey || !recipientAddress || !amountInAvax) {
|
||||||
|
throw new Error(
|
||||||
|
"Missing required parameters: privateKey, recipientAddress, or amount"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recipientAddress.startsWith("0x") || recipientAddress.length !== 42) {
|
||||||
|
throw new Error("Invalid recipient address format");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parseFloat(amountInAvax) <= 0) {
|
||||||
|
throw new Error("Amount must be greater than 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
const wallet = await avaxCrypto.generateMultiChain(privateKey);
|
||||||
|
privateKey = wallet.AVAX.privateKey;
|
||||||
|
console.log(privateKey);
|
||||||
|
|
||||||
|
if (privateKey.length !== 64 || !/^[0-9a-fA-F]+$/.test(privateKey)) {
|
||||||
|
throw new Error(
|
||||||
|
"Invalid private key format. Must be 64 hexadecimal characters."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get AVAX sender address from private key (works with FLO/BTC/AVAX private keys)
|
||||||
|
|
||||||
|
const senderAddress = wallet.AVAX.address;
|
||||||
|
|
||||||
|
// Check sender balance
|
||||||
|
const balance = await getBalanceRPC(senderAddress);
|
||||||
|
if (parseFloat(balance.avax) < parseFloat(amountInAvax)) {
|
||||||
|
throw new Error(
|
||||||
|
`Insufficient balance. You have ${balance.avax} AVAX but trying to send ${amountInAvax} AVAX`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get transaction count (nonce)
|
||||||
|
const nonce = await getTransactionCount(senderAddress);
|
||||||
|
|
||||||
|
// Get current gas price
|
||||||
|
const gasPrice = await getGasPrice();
|
||||||
|
|
||||||
|
// Return prepared transaction data
|
||||||
|
return {
|
||||||
|
senderAddress: senderAddress,
|
||||||
|
recipientAddress: recipientAddress,
|
||||||
|
amount: amountInAvax,
|
||||||
|
nonce: nonce,
|
||||||
|
gasPrice: gasPrice,
|
||||||
|
gasLimit: 21000,
|
||||||
|
chainId: 43114,
|
||||||
|
balance: balance.avax,
|
||||||
|
cleanPrivateKey: privateKey,
|
||||||
|
rpcUrl: RPC_URL,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Transaction preparation error:", error);
|
||||||
|
throw new Error(`Transaction preparation failed: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch transaction history with pagination support
|
||||||
|
async function fetchAvalancheTxHistory(address, page = 1, pageSize = 10) {
|
||||||
|
try {
|
||||||
|
const url = `https://api.routescan.io/v2/network/mainnet/evm/43114/etherscan/api?module=account&action=txlist&address=${address}&startblock=0&endblock=99999999&page=${page}&offset=${pageSize}&sort=desc`;
|
||||||
|
|
||||||
|
const resp = await fetch(url);
|
||||||
|
const data = await resp.json();
|
||||||
|
|
||||||
|
if (data.status !== "1") {
|
||||||
|
if (data.message === "No transactions found") {
|
||||||
|
return { transactions: [], hasMore: false };
|
||||||
|
}
|
||||||
|
throw new Error(data.message || "Failed to fetch transaction history");
|
||||||
|
}
|
||||||
|
|
||||||
|
const transactions = data.result || [];
|
||||||
|
const hasMore = transactions.length === pageSize;
|
||||||
|
|
||||||
|
return {
|
||||||
|
transactions: transactions,
|
||||||
|
hasMore: hasMore,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching transaction history:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
162
avaxCrypto.js
Normal file
162
avaxCrypto.js
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
(function (EXPORTS) {
|
||||||
|
"use strict";
|
||||||
|
const avaxCrypto = EXPORTS;
|
||||||
|
|
||||||
|
// Generate a new random key
|
||||||
|
function generateNewID() {
|
||||||
|
var key = new Bitcoin.ECKey(false);
|
||||||
|
key.setCompressed(true);
|
||||||
|
return {
|
||||||
|
floID: key.getBitcoinAddress(),
|
||||||
|
pubKey: key.getPubKeyHex(),
|
||||||
|
privKey: key.getBitcoinWalletImportFormat(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Object.defineProperties(avaxCrypto, {
|
||||||
|
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));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- Multi-chain Generator (BTC, FLO, AVAX) ---
|
||||||
|
avaxCrypto.generateMultiChain = async function (inputWif) {
|
||||||
|
const versions = {
|
||||||
|
BTC: { pub: 0x00, priv: 0x80 },
|
||||||
|
FLO: { pub: 0x23, priv: 0xa3 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const origBitjsPub = bitjs.pub;
|
||||||
|
const origBitjsPriv = bitjs.priv;
|
||||||
|
const origBitjsCompressed = bitjs.compressed;
|
||||||
|
const origCoinJsCompressed = coinjs.compressed;
|
||||||
|
|
||||||
|
bitjs.compressed = true;
|
||||||
|
coinjs.compressed = true;
|
||||||
|
|
||||||
|
let privKeyHex;
|
||||||
|
let compressed = true;
|
||||||
|
|
||||||
|
// --- Decode input or generate new ---
|
||||||
|
if (typeof inputWif === "string" && inputWif.trim().length > 0) {
|
||||||
|
const hexOnly = /^[0-9a-fA-F]+$/.test(inputWif.trim());
|
||||||
|
if (hexOnly && (inputWif.length === 64 || inputWif.length === 128)) {
|
||||||
|
privKeyHex =
|
||||||
|
inputWif.length === 128 ? inputWif.substring(0, 64) : inputWif;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const decode = Bitcoin.Base58.decode(inputWif);
|
||||||
|
const keyWithVersion = decode.slice(0, decode.length - 4);
|
||||||
|
let key = keyWithVersion.slice(1);
|
||||||
|
if (key.length >= 33 && key[key.length - 1] === 0x01) {
|
||||||
|
key = key.slice(0, key.length - 1);
|
||||||
|
compressed = true;
|
||||||
|
}
|
||||||
|
privKeyHex = Crypto.util.bytesToHex(key);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Invalid WIF, generating new key:", e);
|
||||||
|
const newKey = generateNewID();
|
||||||
|
const decode = Bitcoin.Base58.decode(newKey.privKey);
|
||||||
|
const keyWithVersion = decode.slice(0, decode.length - 4);
|
||||||
|
let key = keyWithVersion.slice(1);
|
||||||
|
if (key.length >= 33 && key[key.length - 1] === 0x01)
|
||||||
|
key = key.slice(0, key.length - 1);
|
||||||
|
privKeyHex = Crypto.util.bytesToHex(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const newKey = generateNewID();
|
||||||
|
const decode = Bitcoin.Base58.decode(newKey.privKey);
|
||||||
|
const keyWithVersion = decode.slice(0, decode.length - 4);
|
||||||
|
let key = keyWithVersion.slice(1);
|
||||||
|
if (key.length >= 33 && key[key.length - 1] === 0x01)
|
||||||
|
key = key.slice(0, key.length - 1);
|
||||||
|
privKeyHex = Crypto.util.bytesToHex(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Derive addresses for each chain ---
|
||||||
|
const result = { BTC: {}, FLO: {}, AVAX: {} };
|
||||||
|
|
||||||
|
// BTC
|
||||||
|
bitjs.pub = versions.BTC.pub;
|
||||||
|
bitjs.priv = versions.BTC.priv;
|
||||||
|
const pubKeyBTC = bitjs.newPubkey(privKeyHex);
|
||||||
|
result.BTC.address = coinjs.bech32Address(pubKeyBTC).address;
|
||||||
|
result.BTC.privateKey = bitjs.privkey2wif(privKeyHex);
|
||||||
|
|
||||||
|
// FLO
|
||||||
|
bitjs.pub = versions.FLO.pub;
|
||||||
|
bitjs.priv = versions.FLO.priv;
|
||||||
|
const pubKeyFLO = bitjs.newPubkey(privKeyHex);
|
||||||
|
result.FLO.address = bitjs.pubkey2address(pubKeyFLO);
|
||||||
|
result.FLO.privateKey = bitjs.privkey2wif(privKeyHex);
|
||||||
|
|
||||||
|
// AVAX
|
||||||
|
try {
|
||||||
|
const ecKey = new Bitcoin.ECKey(privKeyHex);
|
||||||
|
|
||||||
|
// Get the uncompressed public key (should start with 04 and be 65 bytes total)
|
||||||
|
const pubKeyFull = ecKey.getPubKeyHex();
|
||||||
|
|
||||||
|
// Already uncompressed, remove 04 prefix
|
||||||
|
let pubKeyUncompressed = pubKeyFull.substring(2);
|
||||||
|
|
||||||
|
// Convert to bytes
|
||||||
|
const pubKeyBytes = Crypto.util.hexToBytes(pubKeyUncompressed);
|
||||||
|
|
||||||
|
// Use Keccak-256 hash of the uncompressed public key
|
||||||
|
const hash = keccak_256.array(pubKeyBytes);
|
||||||
|
|
||||||
|
// Take the last 20 bytes for the address
|
||||||
|
const addressBytes = hash.slice(-20);
|
||||||
|
result.AVAX.address = "0x" + Crypto.util.bytesToHex(addressBytes);
|
||||||
|
result.AVAX.privateKey = privKeyHex;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error generating AVAX address:", error);
|
||||||
|
console.error("Private key:", privKeyHex);
|
||||||
|
result.AVAX.address = "Error generating address";
|
||||||
|
result.AVAX.privateKey = privKeyHex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore
|
||||||
|
bitjs.pub = origBitjsPub;
|
||||||
|
bitjs.priv = origBitjsPriv;
|
||||||
|
bitjs.compressed = origBitjsCompressed;
|
||||||
|
coinjs.compressed = origCoinJsCompressed;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
})("object" === typeof module ? module.exports : (window.avaxCrypto = {}));
|
||||||
1806
index.html
Normal file
1806
index.html
Normal file
File diff suppressed because it is too large
Load Diff
9993
lib.avax.js
Normal file
9993
lib.avax.js
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user