refactor: move brother_node development artifact to dev/test-nodes subdirectory

Development Artifact Cleanup:
 BROTHER_NODE REORGANIZATION: Moved development test node to appropriate location
- dev/test-nodes/brother_node/: Moved from root directory for better organization
- Contains development configuration, test logs, and test chain data
- No impact on production systems - purely development/testing artifact

 DEVELOPMENT ARTIFACTS IDENTIFIED:
- Chain ID: aitbc-brother-chain (test/development chain)
- Ports: 8010 (P2P) and 8011 (RPC) - different from production
- Environment: .env file with test configuration
- Logs: rpc.log and node.log from development testing session (March 15, 2026)

 ROOT DIRECTORY CLEANUP: Removed development clutter from production directory
- brother_node/ moved to dev/test-nodes/brother_node/
- Root directory now contains only production-ready components
- Development artifacts properly organized in dev/ subdirectory

DIRECTORY STRUCTURE IMPROVEMENT:
📁 dev/test-nodes/: Development and testing node configurations
🏗️ Root Directory: Clean production structure with only essential components
🧪 Development Isolation: Test environments separated from production

BENEFITS:
 Clean Production Directory: No development artifacts in root
 Better Organization: Development nodes grouped in dev/ subdirectory
 Clear Separation: Production vs development environments clearly distinguished
 Maintainability: Easier to identify and manage development components

RESULT: Successfully moved brother_node development artifact to dev/test-nodes/ subdirectory, cleaning up the root directory while preserving development testing environment for future use.
This commit is contained in:
2026-03-30 17:09:06 +02:00
parent bf730dcb4a
commit 816e258d4c
11734 changed files with 2001707 additions and 0 deletions

246
dev/env/node_modules/micro-eth-signer/esm/net/archive.d.ts generated vendored Executable file
View File

@@ -0,0 +1,246 @@
import { TxVersions, type AccessList } from '../tx.ts';
import { type IWeb3Provider, type Web3CallArgs } from '../utils.ts';
declare const CONTRACT_CAPABILITIES: Record<string, string>;
export type BlockInfo = {
baseFeePerGas: bigint;
difficulty: bigint;
extraData: string;
gasLimit: bigint;
gasUsed: bigint;
hash: string;
logsBloom: string;
miner: string;
mixHash: string;
nonce: string;
number: number;
parentHash: string;
receiptsRoot: string;
sha3Uncles: string;
size: number;
stateRoot: string;
timestamp: number;
totalDifficulty?: bigint;
transactions: string[];
transactionsRoot: string;
uncles: string[];
};
export type Action = {
action: {
from: string;
callType: string;
gas: bigint;
input: string;
to: string;
value: bigint;
};
blockHash: string;
blockNumber: number;
result: {
gasUsed: bigint;
output: string;
};
subtraces: number;
traceAddress: string[];
transactionHash: string;
transactionPosition: number;
type: string;
};
export type Log = {
address: string;
topics: string[];
data: string;
blockNumber: number;
transactionHash: string;
transactionIndex: number;
blockHash: string;
logIndex: number;
removed: boolean;
};
export type TxInfo = {
blockHash: string;
blockNumber: number;
hash: string;
accessList?: AccessList;
transactionIndex: number;
type: number;
nonce: bigint;
input: string;
r: bigint;
s: bigint;
chainId: bigint;
v: bigint;
yParity?: string;
gas: bigint;
maxPriorityFeePerGas?: bigint;
from: string;
to: string;
maxFeePerGas?: bigint;
value: bigint;
gasPrice: bigint;
maxFeePerBlobGas?: bigint;
blobVersionedHashes?: string[];
};
export type TxReceipt = {
transactionHash: string;
blockHash: string;
blockNumber: number;
logsBloom: string;
gasUsed: bigint;
contractAddress: string | null;
cumulativeGasUsed: bigint;
transactionIndex: number;
from: string;
to: string;
type: number;
effectiveGasPrice: bigint;
logs: Log[];
status: number;
blobGasPrice?: bigint;
blobGasUsed?: bigint;
};
export type Unspent = {
symbol: 'ETH';
decimals: number;
balance: bigint;
nonce: number;
active: boolean;
};
type ERC20Token = {
abi: 'ERC20';
name?: string;
symbol?: string;
decimals?: number;
totalSupply: bigint;
};
type ERC721Token = {
abi: 'ERC721';
name?: string;
symbol?: string;
totalSupply?: bigint;
enumerable?: boolean;
metadata?: boolean;
};
type ERC1155Token = {
abi: 'ERC1155';
};
export type TokenInfo = {
contract: string;
} & (ERC20Token | ERC721Token | ERC1155Token);
type TokenError = {
contract: string;
error: string;
};
type TokenBalanceSingle = Map<bigint, bigint>;
export type TokenBalances = Record<string, TokenBalanceSingle | TokenError>;
export type Topics = (string | null | (string | null)[])[];
export type Transfer = {
from: string;
to?: string;
value: bigint;
};
export type TokenTransfer = TokenInfo & {
from: string;
to: string;
tokens: Map<bigint, bigint>;
};
export type TxTransfers = {
hash: string;
timestamp?: number;
block?: number;
transfers: Transfer[];
tokenTransfers: TokenTransfer[];
reverted: boolean;
info: {
type: keyof typeof TxVersions;
info: TxInfo;
receipt: TxReceipt;
raw?: string;
block: BlockInfo;
actions: Action[];
};
};
/**
* Callbacks are needed, because we want to call getTx / getBlock / getTokenInfo
* requests as fast as possible, to reduce amount of sequential execution.
* If we retrieve 10 pages of transactions, we can call per tx
* callbacks for transaction from first page before all other pages fetched.
*
* Ensure caching: they can be called multiple times for same tx / block.
*/
export type Callbacks = {
txCallback?: (txHash: string) => void;
blockCallback?: (blockNum: number) => void;
contractCallback?: (contrct: string) => void;
};
export type Pagination = {
fromBlock?: number;
toBlock?: number;
};
export type TraceOpts = Callbacks & Pagination & {
perRequest?: number;
limitTrace?: number;
};
export type LogOpts = Callbacks & (Pagination | {
fromBlock: number;
toBlock: number;
limitLogs: number;
});
export type Balances = {
balances: Record<string, bigint>;
tokenBalances: Record<string, Record<string, bigint>>;
};
export type TxInfoOpts = Callbacks & {
ignoreTxRebuildErrors?: boolean;
};
export type TxAllowances = Record<string, Record<string, bigint>>;
export type JsonrpcInterface = {
call: (method: string, ...args: any[]) => Promise<any>;
};
/**
* Transaction-related code around Web3Provider.
* High-level methods are `height`, `unspent`, `transfers`, `allowances` and `tokenBalances`.
*
* Low-level methods are `blockInfo`, `internalTransactions`, `ethLogs`, `tokenTransfers`, `wethTransfers`,
* `tokenInfo` and `txInfo`.
*/
export declare class Web3Provider implements IWeb3Provider {
private rpc;
constructor(rpc: JsonrpcInterface);
call(method: string, ...args: any[]): Promise<any>;
ethCall(args: Web3CallArgs, tag?: string): Promise<any>;
estimateGas(args: Web3CallArgs, tag?: string): Promise<bigint>;
blockInfo(block: number): Promise<BlockInfo>;
unspent(address: string): Promise<Unspent>;
height(): Promise<number>;
traceFilterSingle(address: string, opts?: TraceOpts): Promise<any>;
internalTransactions(address: string, opts?: TraceOpts): Promise<any[]>;
contractCapabilities(address: string, capabilities?: typeof CONTRACT_CAPABILITIES): Promise<{
[k: string]: boolean;
}>;
ethLogsSingle(topics: Topics, opts: LogOpts): Promise<Log[]>;
ethLogs(topics: Topics, opts?: LogOpts): Promise<Log[]>;
tokenTransfers(address: string, opts?: LogOpts): Promise<[Log[], Log[]]>;
wethTransfers(address: string, opts?: LogOpts): Promise<[Log[]]>;
erc1155Transfers(address: string, opts?: LogOpts): Promise<[Log[], Log[], Log[], Log[]]>;
txInfo(txHash: string, opts?: TxInfoOpts): Promise<{
type: 'legacy' | 'eip2930' | 'eip1559' | 'eip4844' | 'eip7702';
info: any;
receipt: any;
raw: string | undefined;
}>;
tokenInfo(contract: string): Promise<TokenInfo | TokenError>;
private tokenBalanceSingle;
tokenURI(token: TokenInfo | TokenError | string, tokenId: bigint): Promise<string | TokenError>;
tokenBalances(address: string, tokens: string[], tokenIds?: Record<string, Set<bigint>>): Promise<TokenBalances>;
private decodeTokenTransfer;
transfers(address: string, opts?: TraceOpts & LogOpts): Promise<TxTransfers[]>;
allowances(address: string, opts?: LogOpts): Promise<TxAllowances>;
}
/**
* Calculates balances at specific point in time after tx.
* Also, useful as a sanity check in case we've missed something.
* Info from multiple addresses can be merged (sort everything first).
*/
export declare function calcTransfersDiff(transfers: TxTransfers[]): (TxTransfers & Balances)[];
export {};
//# sourceMappingURL=archive.d.ts.map

File diff suppressed because one or more lines are too long

767
dev/env/node_modules/micro-eth-signer/esm/net/archive.js generated vendored Executable file
View File

@@ -0,0 +1,767 @@
import { ERC1155, ERC20, ERC721, WETH, createContract, events } from "../abi/index.js";
import { Transaction } from "../index.js";
import { TxVersions, legacySig } from "../tx.js";
import { amounts, ethHex, hexToNumber } from "../utils.js";
/*
Methods to fetch list of transactions from any ETH node RPC.
It should be easy. However, this is sparta^W ethereum, so, prepare to suffer.
The network is not directly called: `ArchiveNodeProvider#rpc` calls `Web3Provider`.
- There is no simple & fast API inside nodes, all external API create their own namespace for this
- API is different between nodes: erigon uses streaming, other nodes use pagination
- Recently, Erigon have been also adding pagination
- For token transactions: download block headers, look at bloom filter, download affected blocks
- There is a good `getLogs` API for contracts, but nothing for ETH transfers
- `trace_filter` is slow: it not only finds the transaction, but also executes them
- It's good that it allows to get internal transactions
- The whole thing could be 10x simpler if there was an event in logs for ETH transfer
- For most cases, we only need to see last transactions and know blocks of last txs, which is 20x faster
- This creates a lot of requests to node (2 per tx, 1 per block, and some more depends on block range limits)
Recommended software:
- eth-nodes-for-rent are bad, because of their limits and timeouts
- erigon nodes are fast, taking ~15 seconds per batch
- reth has 100-block limit for trace_filter, requiring 190k requests just get transactions
*/
// Utils
const ethNum = (n) => `0x${!n ? '0' : n.toString(16).replace(/^0+/, '')}`;
const ERC_TRANSFER = events(ERC20).Transfer;
const WETH_DEPOSIT = events(WETH).Deposit;
const WETH_WITHDRAW = events(WETH).Withdrawal;
const ERC721_TRANSFER = events(ERC721).Transfer;
const ERC1155_SINGLE = events(ERC1155).TransferSingle;
const ERC1155_BATCH = events(ERC1155).TransferBatch;
const ERC165 = [
// function supportsInterface(bytes4 interfaceID) external view returns (bool);
{
type: 'function',
name: 'supportsInterface',
inputs: [{ name: 'interfaceID', type: 'bytes4' }],
outputs: [{ type: 'bool' }],
},
];
const CONTRACT_CAPABILITIES = {
erc165: '0x01ffc9a7',
erc165_check: '0xffffffff',
erc20: '0x36372b07',
erc721: '0x80ac58cd',
erc721_metadata: '0x5b5e139f',
erc721_enumerable: '0x780e9d63',
erc1155: '0xd9b67a26',
erc1155_tokenreceiver: '0x4e2312e0',
erc1155_metadata: '0x0e89341c',
};
function group(items, s) {
let res = {};
for (let i of items) {
const key = typeof s === 'function' ? s(i) : i[s];
if (!res[key])
res[key] = [];
res[key].push(i);
}
return res;
}
function fixBlock(block) {
block.timestamp = Number(block.timestamp) * 1000;
block.size = Number(block.size);
if (block.number && block.number !== null)
block.number = Number(block.number);
for (const i of [
'baseFeePerGas',
'difficulty',
'gasLimit',
'gasUsed',
'totalDifficulty',
]) {
if (block[i] && block[i] !== null)
block[i] = BigInt(block[i]);
}
}
function fixAction(action, opts = {}) {
action.action.value = BigInt(action.action.value);
action.action.gas = BigInt(action.action.gas);
action.result.gasUsed = BigInt(action.result.gasUsed);
if (opts.txCallback)
opts.txCallback(action.transactionHash);
if (opts.blockCallback)
opts.blockCallback(action.blockNumber);
}
// Fixes type of network response inplace
function fixLog(log, opts = {}) {
log.blockNumber = Number(log.blockNumber);
log.transactionIndex = Number(log.transactionIndex);
log.logIndex = Number(log.logIndex);
if (opts.txCallback)
opts.txCallback(log.transactionHash);
if (opts.blockCallback)
opts.blockCallback(log.blockNumber);
if (opts.contractCallback)
opts.contractCallback(log.address);
return log;
}
function fixTxInfo(info) {
for (const i of ['blockNumber', 'type', 'transactionIndex'])
info[i] = Number(info[i]);
for (const i of [
'nonce',
'r',
's',
'chainId',
'v',
'gas',
'maxPriorityFeePerGas',
'maxFeePerGas',
'value',
'gasPrice',
'maxFeePerBlobGas',
]) {
if (info[i] !== undefined && info[i] !== null)
info[i] = BigInt(info[i]);
}
return info;
}
function fixTxReceipt(receipt) {
for (const i of ['blockNumber', 'type', 'transactionIndex', 'status'])
receipt[i] = Number(receipt[i]);
for (const i of [
'gasUsed',
'cumulativeGasUsed',
'effectiveGasPrice',
'blobGasPrice',
'blobGasUsed',
]) {
if (receipt[i] !== undefined)
receipt[i] = BigInt(receipt[i]);
}
for (const log of receipt.logs)
fixLog(log);
return receipt;
}
function validateCallbacks(opts) {
for (const i of ['txCallback', 'blockCallback', 'contractCallback']) {
if (opts[i] !== undefined && typeof opts[i] !== 'function')
throw new Error(`validateCallbacks: ${i} should be function`);
}
}
function validatePagination(opts) {
for (const i of ['fromBlock', 'toBlock']) {
if (opts[i] === undefined || Number.isSafeInteger(opts[i]))
continue;
throw new Error(`validatePagination: wrong field ${i}=${opts[i]}. Should be integer or undefined`);
}
}
function validateTraceOpts(opts) {
validatePagination(opts);
for (const i of ['perRequest', 'limitTrace']) {
if (opts[i] === undefined || Number.isSafeInteger(opts[i]))
continue;
throw new Error(`validateTraceOpts: wrong field ${i}=${opts[i]}. Should be integer or undefined`);
}
if (opts.limitTrace !== undefined) {
if (opts.fromBlock === undefined || opts.toBlock === undefined)
throw new Error('validateTraceOpts: fromBlock/toBlock required if limitTrace is present');
}
validateCallbacks(opts);
}
function validateLogOpts(opts) {
validatePagination(opts);
for (const i of ['limitLogs']) {
if (opts[i] === undefined || Number.isSafeInteger(opts[i]))
continue;
throw new Error(`validateLogOpts: wrong field ${i}=${opts[i]}. Should be integer or undefined`);
}
if (opts.limitLogs !== undefined) {
if (opts.fromBlock === undefined || opts.toBlock === undefined)
throw new Error('validateLogOpts: fromBlock/toBlock required if limitLogs is present');
}
validateCallbacks(opts);
}
// Promise.all for objects, undefined if error
async function wait(obj) {
const keys = Object.keys(obj);
const p = await Promise.allSettled(Object.values(obj));
const res = p.map((r, i) => [keys[i], r.status === 'fulfilled' ? r.value : undefined]);
return Object.fromEntries(res);
}
const isReverted = (e) => e instanceof Error && e.message.toLowerCase().includes('revert');
/**
* Transaction-related code around Web3Provider.
* High-level methods are `height`, `unspent`, `transfers`, `allowances` and `tokenBalances`.
*
* Low-level methods are `blockInfo`, `internalTransactions`, `ethLogs`, `tokenTransfers`, `wethTransfers`,
* `tokenInfo` and `txInfo`.
*/
export class Web3Provider {
constructor(rpc) {
this.rpc = rpc;
}
call(method, ...args) {
return this.rpc.call(method, ...args);
}
ethCall(args, tag = 'latest') {
return this.rpc.call('eth_call', args, tag);
}
async estimateGas(args, tag = 'latest') {
return hexToNumber(await this.rpc.call('eth_estimateGas', args, tag));
}
// Timestamp is available only inside blocks
async blockInfo(block) {
const res = await this.call('eth_getBlockByNumber', ethNum(block), false);
fixBlock(res);
return res;
}
async unspent(address) {
let [balance, nonce] = await Promise.all([
this.call('eth_getBalance', address, 'latest'),
this.call('eth_getTransactionCount', address, 'latest'),
]);
balance = BigInt(balance);
nonce = BigInt(nonce);
return {
symbol: 'ETH',
decimals: amounts.ETH_PRECISION,
balance,
nonce,
// Note: account can be active even if nonce!==0!
active: balance > 0 || nonce !== 0,
};
}
async height() {
return Number.parseInt(await this.call('eth_blockNumber'));
}
async traceFilterSingle(address, opts = {}) {
const res = await this.call('trace_filter', {
fromBlock: ethNum(opts.fromBlock),
toBlock: ethNum(opts.toBlock),
toAddress: [address],
fromAddress: [address],
});
for (const action of res)
fixAction(action, opts);
return res;
}
async internalTransactions(address, opts = {}) {
if (typeof address !== 'string')
throw new Error('internalTransactions: wrong address');
validateTraceOpts(opts);
// For reth
if (opts.limitTrace) {
const promises = [];
for (let i = opts.fromBlock; i <= opts.toBlock; i += opts.limitTrace)
promises.push(this.traceFilterSingle(address, { fromBlock: i, toBlock: i + opts.limitTrace }));
const out = [];
for (const i of await Promise.all(promises))
out.push(...i);
return out;
}
let lastBlock = opts.fromBlock || 0;
const perBlock = {};
const out = [];
for (;;) {
const params = {
fromBlock: ethNum(lastBlock),
toAddress: [address],
fromAddress: [address],
after: perBlock[lastBlock] || 0, // we cannot just store after, since fromBlock changes to last block
};
if (opts.toBlock !== undefined)
params.toBlock = ethNum(opts.toBlock);
if (opts.perRequest !== undefined)
params.count = opts.perRequest;
const res = await this.call('trace_filter', params);
if (!res.length)
break;
for (const action of res) {
fixAction(action, opts);
if (perBlock[action.blockNumber] === undefined)
perBlock[action.blockNumber] = 0;
perBlock[action.blockNumber]++;
out.push(action);
lastBlock = Math.max(lastBlock, action.blockNumber);
}
}
return out;
}
async contractCapabilities(address, capabilities = {}) {
const all = { ...CONTRACT_CAPABILITIES, ...capabilities };
let c = createContract(ERC165, this, address);
const keys = Object.keys(all);
// TODO: what about revert?
// if reverted -> all capabilities disabled
try {
const promises = await Promise.all(Object.values(all).map((i) => c.supportsInterface.call(ethHex.decode(i))));
const res = Object.fromEntries(keys.map((k, i) => [k, promises[i]]));
// if somehow there is same method, but it doesn't support erc165, then it is different method!
// erc165_check if sailsafe when there is method that always returns true
if (!res.erc165 || res.erc165_check)
for (const k in res)
res[k] = false;
return res;
}
catch (e) {
// If execution reverted: contract doesn't support ERC165
if (isReverted(e))
return Object.fromEntries(keys.map((k) => [k, false]));
throw e;
}
}
async ethLogsSingle(topics, opts) {
const req = { topics, fromBlock: ethNum(opts.fromBlock || 0) };
if (opts.toBlock !== undefined)
req.toBlock = ethNum(opts.toBlock);
const res = await this.call('eth_getLogs', req);
return res.map((i) => fixLog(i, opts));
}
async ethLogs(topics, opts = {}) {
validateLogOpts(opts);
const fromBlock = opts.fromBlock || 0;
if (!('limitLogs' in opts))
return this.ethLogsSingle(topics, opts);
const promises = [];
for (let i = fromBlock; i <= opts.toBlock; i += opts.limitLogs)
promises.push(this.ethLogsSingle(topics, { fromBlock: i, toBlock: i + opts.limitLogs }));
const out = [];
for (const i of await Promise.all(promises))
out.push(...i);
return out;
}
// NOTE: this is very low-level methods that return parts used for .transfers method,
// you will need to decode data yourself.
async tokenTransfers(address, opts = {}) {
if (typeof address !== 'string')
throw new Error('tokenTransfers: wrong address');
validateLogOpts(opts);
// If we want incoming and outgoing token transfers we need to call both
return await Promise.all([
this.ethLogs(ERC_TRANSFER.topics({ from: address, to: null, value: null }), opts), // From
this.ethLogs(ERC_TRANSFER.topics({ from: null, to: address, value: null }), opts), // To
]);
}
async wethTransfers(address, opts = {}) {
if (typeof address !== 'string')
throw new Error('tokenTransfers: wrong address');
validateLogOpts(opts);
const depositTopic = WETH_DEPOSIT.topics({ dst: address, wad: null });
const withdrawTopic = WETH_WITHDRAW.topics({ src: address, wad: null });
// OR query
return await Promise.all([
this.ethLogs([[depositTopic[0], withdrawTopic[0]], depositTopic[1]], opts),
]);
}
async erc1155Transfers(address, opts = {}) {
if (typeof address !== 'string')
throw new Error('tokenTransfers: wrong address');
validateLogOpts(opts);
return await Promise.all([
// Single
this.ethLogs(ERC1155_SINGLE.topics({ operator: null, from: address, to: null, id: null, value: null }), opts),
this.ethLogs(ERC1155_SINGLE.topics({ operator: null, from: null, to: address, id: null, value: null }), opts),
// Batch
this.ethLogs(ERC1155_BATCH.topics({ operator: null, from: address, to: null, ids: null, values: null }), opts),
this.ethLogs(ERC1155_BATCH.topics({ operator: null, from: null, to: address, ids: null, values: null }), opts),
]);
}
async txInfo(txHash, opts = {}) {
let [info, receipt] = await Promise.all([
this.call('eth_getTransactionByHash', txHash),
this.call('eth_getTransactionReceipt', txHash),
]);
info = fixTxInfo(info);
receipt = fixTxReceipt(receipt);
const type = Object.keys(TxVersions)[info.type];
// This is not strictly neccessary, but allows to store tx info in very compact format and remove unneccessary fields
// Also, there is additional validation that node returned actual with correct hash/sender and not corrupted stuff.
let raw = undefined;
try {
const rawData = {
nonce: info.nonce,
gasLimit: info.gas,
to: info.to === null ? '0x' : info.to,
value: info.value,
data: info.input,
r: info.r,
s: info.s,
yParity: Number(info.v),
chainId: info.chainId,
};
if (info.accessList)
rawData.accessList = info.accessList;
if (info.maxFeePerBlobGas)
rawData.maxFeePerBlobGas = info.maxFeePerBlobGas;
if (info.blobVersionedHashes)
rawData.blobVersionedHashes = info.blobVersionedHashes;
if (info.maxFeePerGas) {
rawData.maxFeePerGas = info.maxFeePerGas;
rawData.maxPriorityFeePerGas = info.maxPriorityFeePerGas;
}
else if (info.gasPrice)
rawData.gasPrice = info.gasPrice;
if (type === 'legacy')
Object.assign(rawData, legacySig.encode({ v: info.v, r: info.r, s: info.s }));
const tx = new Transaction(type, rawData, false, true);
if (tx.recoverSender().address.toLowerCase() !== info.from.toLowerCase())
throw new Error('txInfo: wrong sender');
if (receipt.transactionHash !== `0x${tx.hash}`)
throw new Error('txInfo: wrong hash');
raw = tx.toHex();
}
catch (err) {
// This can crash if something wrong with our parser or limits, so
// we have option to make network code to work even if rebuilding is crashed
if (!opts.ignoreTxRebuildErrors)
throw err;
}
if (opts.blockCallback && info.blockNumber !== null)
opts.blockCallback(info.blockNumber);
return { type, info, receipt, raw };
}
async tokenInfo(contract) {
const c = createContract(ERC20, this, contract);
const t = await wait({
code: this.call('eth_getCode', contract, 'latest'),
capabilities: this.contractCapabilities(contract),
// We call all stuff at same time to reduce latency (should be done in single req if batched)
name: c.name.call(), // ERC-20 (optional), ERC-721 (metada)
symbol: c.symbol.call(), // ERC-20 (optional), ERC-721 (metadata)
decimals: c.decimals.call(), // ERC-20 (optional), ERC-721 (enumarable)
totalSupply: c.totalSupply.call(), // ERC-20 (required), ERC-721
});
// No code, probably self-destructed
if (t.code === '0x')
return { contract, error: 'not contract or destructed' };
if (t.capabilities && t.capabilities.erc1155) {
// All metadata is inside URI per tokenId to outside network stuff (maybe ipfs), so nothing to do here.
return { contract, abi: 'ERC1155' };
}
if (t.capabilities && t.capabilities.erc721) {
const res = { contract, abi: 'ERC721' };
if (t.capabilities.erc721_metadata) {
if (t.name === undefined)
return { contract, error: 'ERC721+Metadata without name' };
if (t.symbol === undefined)
return { contract, error: 'ERC721+Metadata without symbol' };
Object.assign(res, { name: t.name, symbol: t.symbol, metadata: true });
}
if (t.capabilities.erc721_enumerable) {
if (t.totalSupply === undefined)
return { contract, error: 'ERC721+Enumerable without totalSupply' };
Object.assign(res, { totalSupply: t.totalSupply, enumerable: true });
}
return res;
}
if (t.totalSupply === undefined)
return { contract, error: 'not ERC20 token' }; // If there is no totalSupply, it is not ERC20!
return {
contract,
abi: 'ERC20',
name: t.name,
symbol: t.symbol,
totalSupply: t.totalSupply,
decimals: t.decimals ? Number(t.decimals) : undefined,
};
}
async tokenBalanceSingle(address, token, tokenIds) {
if ('error' in token)
return token;
if (token.abi === 'ERC20') {
const balance = await createContract(ERC20, this, token.contract).balanceOf.call(address);
if (tokenIds && (tokenIds.size > 1 || Array.from(tokenIds)[0] !== 1n)) {
return { contract: token.contract, error: 'unexpected tokenIds for ERC20' };
}
return new Map([[1n, balance]]);
}
else if (token.abi === 'ERC721') {
const c = createContract(ERC721, this, token.contract);
const balance = await c.balanceOf.call(address);
if (!token.enumerable) {
if (!tokenIds) {
if (!balance)
return new Map(); // no tokens owned by user
return {
contract: token.contract,
error: 'erc721 contract not enumerable, but owner has ' + balance + ' tokens',
};
}
// if we cannot enumerate, but has tokenIds, we can check if tokenIds still owned by account
const ids = Array.from(tokenIds);
const owners = await Promise.all(ids.map((i) => c.ownerOf.call(i)));
return new Map(ids.map((i, j) => [i, owners[j].toLowerCase() === address.toLowerCase() ? 1n : 0n]));
}
// if we can fetch tokenIds: always do this
const p = [];
for (let i = 0; i < balance; i++)
p.push(c.tokenOfOwnerByIndex.call({ owner: address, index: BigInt(i) }));
tokenIds = new Set(await Promise.all(p));
const ids = Array.from(tokenIds);
return new Map(ids.map((i) => [i, 1n]));
}
else if (token.abi === 'ERC1155') {
// This is pretty bad standard, because it doesn't allow enumeration of tokenIds for owner
if (!tokenIds)
return { contract: token.contract, error: 'cannot fetch erc1155 without tokenIds' };
const c = createContract(ERC1155, this, token.contract);
const ids = Array.from(tokenIds);
const balances = await c.balanceOfBatch.call({ accounts: ids.map((_) => address), ids });
const res = new Map(ids.map((i, j) => [i, balances[j]]));
return res;
}
throw new Error('unknown token type');
}
async tokenURI(token, tokenId) {
if (typeof token === 'string')
token = await this.tokenInfo(token);
if ('error' in token)
return token;
if (token.abi === 'ERC721') {
const c = createContract(ERC721, this, token.contract);
if (!token.metadata)
return { contract: token.contract, error: 'erc721 without metadata' };
return c.tokenURI.call(tokenId);
}
else if (token.abi === 'ERC1155') {
const c = createContract(ERC1155, this, token.contract);
return c.uri.call(tokenId);
}
return { contract: token.contract, error: 'not supported token type' };
}
async tokenBalances(address, tokens, tokenIds) {
// New API requires data from tokenInfo (which is slow and should be cached).
// But for compat with old API, we do tokenInfo call if contract address (as string) presented
const _tokens = await Promise.all(tokens.map((i) => (typeof i === 'string' ? this.tokenInfo(i) : i)));
const balances = await Promise.all(_tokens.map((i) => this.tokenBalanceSingle(address, i, tokenIds && tokenIds[i.contract])));
return Object.fromEntries(_tokens.map((i, j) => [i.contract, balances[j]]));
}
decodeTokenTransfer(token, log) {
if ('error' in token)
return;
if (token.abi === 'ERC20') {
try {
const decoded = ERC_TRANSFER.decode(log.topics, log.data);
return {
...token,
contract: log.address,
to: decoded.to,
from: decoded.from,
tokens: new Map([[1n, decoded.value]]),
};
}
catch (e) { }
// Weth doesn't issue Transfer event on Deposit/Withdrawal
// NOTE: we don't filter for WETH_CONTRACT here in case of other contracts with similar API or different networks
try {
const decoded = WETH_DEPOSIT.decode(log.topics, log.data);
return {
...token,
contract: log.address,
from: log.address,
to: decoded.dst,
tokens: new Map([[1n, decoded.wad]]),
};
}
catch (e) { }
try {
const decoded = WETH_WITHDRAW.decode(log.topics, log.data);
return {
...token,
contract: log.address,
from: decoded.src,
to: log.address,
tokens: new Map([[1n, decoded.wad]]),
};
}
catch (e) { }
}
else if (token.abi === 'ERC721') {
try {
const decoded = ERC721_TRANSFER.decode(log.topics, log.data);
return {
...token,
from: decoded.from,
to: decoded.to,
tokens: new Map([[decoded.tokenId, 1n]]),
};
}
catch (e) { }
}
else if (token.abi === 'ERC1155') {
try {
const decoded = ERC1155_SINGLE.decode(log.topics, log.data);
return {
...token,
from: decoded.from,
to: decoded.to,
tokens: new Map([[decoded.id, decoded.value]]),
};
}
catch (e) { }
try {
const decoded = ERC1155_BATCH.decode(log.topics, log.data);
return {
...token,
from: decoded.from,
to: decoded.to,
tokens: new Map(decoded.ids.map((i, j) => [i, decoded.values[j]])),
};
}
catch (e) { }
}
return; // unknown token type
}
// We want to get all transactions related to address, that means:
// - from or to equals address in tx
// - any internal tx from or to equals address in tx
// - any erc20 token transfer which hash address in src or dst
// - erc721 is exactly same function signature as erc20 (need to detect after getting transactions)
// - erc1155: from/to + single/batch
// trace_filter (web3) returns information only for first two cases, most of explorers returns only first case.
async transfers(address, opts = {}) {
const txCache = {};
const blockCache = {};
const tokenCache = {};
const _opts = {
...opts,
txCallback: (txHash) => {
if (txCache[txHash])
return;
txCache[txHash] = this.txInfo(txHash, opts);
},
blockCallback: (blockNumber) => {
if (blockCache[blockNumber])
return;
blockCache[blockNumber] = this.blockInfo(blockNumber);
},
contractCallback: (address) => {
if (tokenCache[address])
return;
tokenCache[address] = this.tokenInfo(address);
},
};
if (!_opts.fromBlock)
_opts.fromBlock = 0;
// This runs in parallel and executes callbacks
// Note, we ignore logs and weth, but they will call callbacks and fetch related
const [actions, _logs, _weth] = await Promise.all([
this.internalTransactions(address, _opts),
this.tokenTransfers(address, _opts),
this.wethTransfers(address, _opts),
this.erc1155Transfers(address, _opts),
]);
const mapCache = async (cache) => {
const keys = Object.keys(cache);
const values = await Promise.all(Object.values(cache));
return Object.fromEntries(values.map((v, i) => [keys[i], v]));
};
// it is ok to do this sequentially, since promises already started and probably resolved at this point
const blocks = await mapCache(blockCache);
const tx = await mapCache(txCache);
const tokens = await mapCache(tokenCache);
const actionPerTx = group(actions, 'transactionHash');
// Sort transactions by [blockNumber, transactionIndex]
const _txHashes = Object.entries(tx).map(([k, v]) => [k, v.info.blockNumber, v.info.transactionIndex]);
_txHashes.sort((a, b) => (a[1] !== b[1] ? a[1] - b[1] : a[2] - b[2]));
const txHashes = _txHashes.map((i) => i[0]);
return txHashes.map((txHash) => {
const { info, receipt } = tx[txHash];
const actions = actionPerTx[txHash];
const block = info.blockNumber !== null ? blocks[info.blockNumber] : undefined;
const transfers = [];
if (actions) {
for (const a of actions)
transfers.push({ from: a.action.from, to: a.action.to, value: a.action.value });
}
else {
// If we have action, it was call to contract and transfer from tx is already added
transfers.push({ from: info.from, to: info.to, value: info.value });
}
// cumulativeGasUsed includes all transactions before that in block, so useless. gasUsed is correct even for internal transactions
transfers.push({ from: info.from, value: receipt.gasUsed * receipt.effectiveGasPrice });
// Tokens
const tokenTransfers = [];
for (const log of receipt.logs) {
const tokenInfo = tokens[log.address];
if (!tokenInfo)
continue;
const tt = this.decodeTokenTransfer(tokenInfo, log);
if (tt)
tokenTransfers.push(tt);
}
return {
hash: txHash,
timestamp: block.timestamp,
block: info.blockNumber !== null ? info.blockNumber : undefined,
reverted: !receipt.status,
transfers,
tokenTransfers,
info: { ...tx[txHash], block, actions },
};
});
}
async allowances(address, opts = {}) {
const approval = events(ERC20).Approval;
// ERC-721/ERC-1155: +ApprovalForAll
// ERC-1761 Scoped Approval for partial with 1155/721?
const topics = approval.topics({ owner: address, spender: null, value: null });
const logs = await this.ethLogs(topics, opts);
// res[tokenContract][spender] = value
const res = {};
for (const l of logs) {
const decoded = approval.decode(l.topics, l.data);
if (decoded.owner.toLowerCase() !== address.toLowerCase())
continue;
if (!res[l.address])
res[l.address] = {};
res[l.address][decoded.spender] = decoded.value;
}
return res;
}
}
/**
* Calculates balances at specific point in time after tx.
* Also, useful as a sanity check in case we've missed something.
* Info from multiple addresses can be merged (sort everything first).
*/
export function calcTransfersDiff(transfers) {
// address -> balance
const balances = {};
// contract -> address -> tokenId -> balance
const tokenBalances = {};
let _0 = BigInt(0);
for (const t of transfers) {
for (const it of t.transfers) {
if (it.from) {
if (balances[it.from] === undefined)
balances[it.from] = _0;
balances[it.from] -= it.value;
}
if (it.to) {
if (balances[it.to] === undefined)
balances[it.to] = _0;
balances[it.to] += it.value;
}
}
for (const tt of t.tokenTransfers) {
if (!tokenBalances[tt.contract])
tokenBalances[tt.contract] = {};
const token = tokenBalances[tt.contract];
for (const [tokenId, value] of tt.tokens) {
if (token[tt.from] === undefined)
token[tt.from] = new Map();
if (token[tt.to] === undefined)
token[tt.to] = new Map();
const fromTokens = token[tt.from];
const toTokens = token[tt.to];
fromTokens.set(tokenId, (fromTokens.get(tokenId) || _0) - value);
toTokens.set(tokenId, (toTokens.get(tokenId) || _0) + value);
}
}
Object.assign(t, {
balances: { ...balances },
// deep copy
tokenBalances: Object.fromEntries(Object.entries(tokenBalances).map(([k, v]) => [k, { ...v }])),
});
}
return transfers;
}
//# sourceMappingURL=archive.js.map

File diff suppressed because one or more lines are too long

14
dev/env/node_modules/micro-eth-signer/esm/net/chainlink.d.ts generated vendored Executable file
View File

@@ -0,0 +1,14 @@
import { type IWeb3Provider } from '../utils.ts';
export declare const TOKENS: Record<string, {
decimals: number;
contract: string;
tokenContract: string;
}>;
export default class Chainlink {
readonly net: IWeb3Provider;
constructor(net: IWeb3Provider);
price(contract: string, decimals: number): Promise<number>;
coinPrice(symbol: string): Promise<number>;
tokenPrice(symbol: string): Promise<number>;
}
//# sourceMappingURL=chainlink.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"chainlink.d.ts","sourceRoot":"","sources":["../../src/net/chainlink.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,aAAa,EAAiB,MAAM,aAAa,CAAC;AAgBhE,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAuQ9F,CAAC;AAEJ,MAAM,CAAC,OAAO,OAAO,SAAS;IAC5B,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;gBAChB,GAAG,EAAE,aAAa;IAGxB,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQ1D,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAe1C,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAKlD"}

313
dev/env/node_modules/micro-eth-signer/esm/net/chainlink.js generated vendored Executable file
View File

@@ -0,0 +1,313 @@
import { createContract, tokenFromSymbol } from "../abi/index.js";
import { createDecimal } from "../utils.js";
const ABI = [
{
type: 'function',
name: 'latestRoundData',
outputs: [
{ name: 'roundId', type: 'uint80' },
{ name: 'answer', type: 'int256' },
{ name: 'startedAt', type: 'uint256' },
{ name: 'updatedAt', type: 'uint256' },
{ name: 'answeredInRound', type: 'uint80' },
],
},
];
export const TOKENS = {
'1INCH': {
decimals: 8,
contract: '0xc929ad75b72593967de83e7f7cda0493458261d9',
tokenContract: '0x111111111117dc0aa78b770fa6a738034120c302',
},
AAPL: {
decimals: 8,
contract: '0x139c8512cde1778e9b9a8e721ce1aebd4dd43587',
tokenContract: '0x7edc9e8a1196259b7c6aba632037a9443d4e14f7',
},
AAVE: {
decimals: 8,
contract: '0x547a514d5e3769680ce22b2361c10ea13619e8a9',
tokenContract: '0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9',
},
ADX: {
decimals: 8,
contract: '0x231e764b44b2c1b7ca171fa8021a24ed520cde10',
tokenContract: '0x4470bb87d77b963a013db939be332f927f2b992e',
},
AKRO: {
decimals: 8,
contract: '0xb23d105df4958b4b81757e12f2151b5b5183520b',
tokenContract: '0x8ab7404063ec4dbcfd4598215992dc3f8ec853d7',
},
AMP: {
decimals: 8,
contract: '0x8797abc4641de76342b8ace9c63e3301dc35e3d8',
tokenContract: '0xff20817765cb7f73d4bde2e66e067e58d11095c2',
},
AMPL: {
decimals: 18,
contract: '0xe20ca8d7546932360e37e9d72c1a47334af57706',
tokenContract: '0xd46ba6d942050d489dbd938a2c909a5d5039a161',
},
AMZN: {
decimals: 8,
contract: '0x8994115d287207144236c13be5e2bdbf6357d9fd',
tokenContract: '0xd6a073d973f95b7ce2ecf2b19224fa12103cf460',
},
ANKR: {
decimals: 8,
contract: '0x7eed379bf00005cfed29fed4009669de9bcc21ce',
tokenContract: '0x8290333cef9e6d528dd5618fb97a76f268f3edd4',
},
BADGER: {
decimals: 8,
contract: '0x66a47b7206130e6ff64854ef0e1edfa237e65339',
tokenContract: '0x3472a5a71965499acd81997a54bba8d852c6e53d',
},
BAND: {
decimals: 8,
contract: '0x919c77acc7373d000b329c1276c76586ed2dd19f',
tokenContract: '0xba11d00c5f74255f56a5e366f4f77f5a186d7f55',
},
BAT: {
decimals: 8,
contract: '0x9441d7556e7820b5ca42082cfa99487d56aca958',
tokenContract: '0x0d8775f648430679a709e98d2b0cb6250d2887ef',
},
BNB: {
decimals: 8,
contract: '0x14e613ac84a31f709eadbdf89c6cc390fdc9540a',
tokenContract: '0xb8c77482e45f1f44de1745f52c74426c631bdd52',
},
BNT: {
decimals: 8,
contract: '0x1e6cf0d433de4fe882a437abc654f58e1e78548c',
tokenContract: '0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c',
},
BTM: {
decimals: 8,
contract: '0x9fccf42d21ab278e205e7bb310d8979f8f4b5751',
tokenContract: '0xcb97e65f07da24d46bcdd078ebebd7c6e6e3d750',
},
BUSD: {
decimals: 8,
contract: '0x833d8eb16d306ed1fbb5d7a2e019e106b960965a',
tokenContract: '0x4fabb145d64652a948d72533023f6e7a623c7c53',
},
COMP: {
decimals: 8,
contract: '0xdbd020caef83efd542f4de03e3cf0c28a4428bd5',
tokenContract: '0xc00e94cb662c3520282e6f5717214004a7f26888',
},
COVER: {
decimals: 8,
contract: '0x0ad50393f11ffac4dd0fe5f1056448ecb75226cf',
tokenContract: '0x4688a8b1f292fdab17e9a90c8bc379dc1dbd8713',
},
CRO: {
decimals: 8,
contract: '0x00cb80cf097d9aa9a3779ad8ee7cf98437eae050',
tokenContract: '0xa0b73e1ff0b80914ab6fe0444e65848c4c34450b',
},
CRV: {
decimals: 8,
contract: '0xcd627aa160a6fa45eb793d19ef54f5062f20f33f',
tokenContract: '0xd533a949740bb3306d119cc777fa900ba034cd52',
},
DAI: {
decimals: 8,
contract: '0xaed0c38402a5d19df6e4c03f4e2dced6e29c1ee9',
tokenContract: '0x60d9564303c70d3f040ea9393d98d94f767d020c',
},
DPI: {
decimals: 8,
contract: '0xd2a593bf7594ace1fad597adb697b5645d5eddb2',
tokenContract: '0x1494ca1f11d487c2bbe4543e90080aeba4ba3c2b',
},
EOS: {
decimals: 8,
contract: '0x10a43289895eaff840e8d45995bba89f9115ecee',
tokenContract: '0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0',
},
FXS: {
decimals: 8,
contract: '0x6ebc52c8c1089be9eb3945c4350b68b8e4c2233f',
tokenContract: '0x3432b6a60d23ca0dfca7761b7ab56459d9c964d0',
},
HT: {
decimals: 8,
contract: '0xe1329b3f6513912caf589659777b66011aee5880',
tokenContract: '0x6f259637dcd74c767781e37bc6133cd6a68aa161',
},
IOST: {
decimals: 8,
contract: '0xd0935838935349401c73a06fcde9d63f719e84e5',
tokenContract: '0xfa1a856cfa3409cfa145fa4e20eb270df3eb21ab',
},
KNC: {
decimals: 8,
contract: '0xf8ff43e991a81e6ec886a3d281a2c6cc19ae70fc',
tokenContract: '0xdd974d5c2e2928dea5f71b9825b8b646686bd200',
},
LINK: {
decimals: 8,
contract: '0x2c1d072e956affc0d435cb7ac38ef18d24d9127c',
tokenContract: '0x514910771af9ca656af840dff83e8264ecf986ca',
},
LRC: {
decimals: 8,
contract: '0xfd33ec6abaa1bdc3d9c6c85f1d6299e5a1a5511f',
tokenContract: '0xef68e7c694f40c8202821edf525de3782458639f',
},
MATIC: {
decimals: 8,
contract: '0x7bac85a8a13a4bcd8abb3eb7d6b4d632c5a57676',
tokenContract: '0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0',
},
MKR: {
decimals: 8,
contract: '0xec1d1b3b0443256cc3860e24a46f108e699484aa',
tokenContract: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2',
},
MTA: {
decimals: 8,
contract: '0xc751e86208f0f8af2d5cd0e29716ca7ad98b5ef5',
tokenContract: '0xa3bed4e1c75d00fa6f4e5e6922db7261b5e9acd2',
},
NFLX: {
decimals: 8,
contract: '0x67c2e69c5272b94af3c90683a9947c39dc605dde',
tokenContract: '0x0a3dc37762f0102175fd43d3871d7fa855626146',
},
NMR: {
decimals: 8,
contract: '0xcc445b35b3636bc7cc7051f4769d8982ed0d449a',
tokenContract: '0x1776e1f26f98b1a5df9cd347953a26dd3cb46671',
},
OCEAN: {
decimals: 8,
contract: '0x7ece4e4e206ed913d991a074a19c192142726797',
tokenContract: '0x967da4048cd07ab37855c090aaf366e4ce1b9f48',
},
OKB: {
decimals: 8,
contract: '0x22134617ae0f6ca8d89451e5ae091c94f7d743dc',
tokenContract: '0x75231f58b43240c9718dd58b4967c5114342a86c',
},
OMG: {
decimals: 8,
contract: '0x7d476f061f8212a8c9317d5784e72b4212436e93',
tokenContract: '0xd26114cd6ee289accf82350c8d8487fedb8a0c07',
},
OXT: {
decimals: 8,
contract: '0xd75aaae4af0c398ca13e2667be57af2cca8b5de6',
tokenContract: '0x4575f41308ec1483f3d399aa9a2826d74da13deb',
},
REN: {
decimals: 8,
contract: '0x0f59666ede214281e956cb3b2d0d69415aff4a01',
tokenContract: '0x408e41876cccdc0f92210600ef50372656052a38',
},
SAND: {
decimals: 8,
contract: '0x35e3f7e558c04ce7eee1629258ecbba03b36ec56',
tokenContract: '0x3845badade8e6dff049820680d1f14bd3903a5d0',
},
SNX: {
decimals: 8,
contract: '0xdc3ea94cd0ac27d9a86c180091e7f78c683d3699',
tokenContract: '0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f',
},
SUSHI: {
decimals: 8,
contract: '0xcc70f09a6cc17553b2e31954cd36e4a2d89501f7',
tokenContract: '0x6b3595068778dd592e39a122f4f5a5cf09c90fe2',
},
SXP: {
decimals: 8,
contract: '0xfb0cfd6c19e25db4a08d8a204a387cea48cc138f',
tokenContract: '0x8ce9137d39326ad0cd6491fb5cc0cba0e089b6a9',
},
UNI: {
decimals: 8,
contract: '0x553303d460ee0afb37edff9be42922d8ff63220e',
tokenContract: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984',
},
USDC: {
decimals: 8,
contract: '0x8fffffd4afb6115b954bd326cbe7b4ba576818f6',
tokenContract: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
},
USDK: {
decimals: 8,
contract: '0xfac81ea9dd29d8e9b212acd6edbeb6de38cb43af',
tokenContract: '0x1c48f86ae57291f7686349f12601910bd8d470bb',
},
USDT: {
decimals: 8,
contract: '0x3e7d1eab13ad0104d2750b8863b489d65364e32d',
tokenContract: '0xdac17f958d2ee523a2206206994597c13d831ec7',
},
YFI: {
decimals: 8,
contract: '0xa027702dbb89fbd58938e4324ac03b58d812b0e1',
tokenContract: '0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e',
},
ZRX: {
decimals: 8,
contract: '0x2885d15b8af22648b98b122b22fdf4d2a56c6023',
tokenContract: '0xe41d2489571d322189246dafa5ebde1f4699f498',
},
SUSD: {
decimals: 8,
contract: '0xad35bd71b9afe6e4bdc266b345c198eadef9ad94',
tokenContract: '0x57ab1e02fee23774580c119740129eac7081e9d3',
},
// Wrapped tokens uses price of original coin
WBTC: {
decimals: 8,
contract: '0xf4030086522a5beea4988f8ca5b36dbc97bee88c',
tokenContract: tokenFromSymbol('WBTC').contract,
},
WETH: {
decimals: 8,
contract: '0x5f4ec3df9cbd43714fe2740f5e3616155c5b8419',
tokenContract: tokenFromSymbol('WETH').contract,
},
};
export default class Chainlink {
constructor(net) {
this.net = net;
}
async price(contract, decimals) {
const prices = createContract(ABI, this.net, contract);
let res = await prices.latestRoundData.call();
const num = Number.parseFloat(createDecimal(decimals).encode(res.answer));
if (Number.isNaN(num))
throw new Error('invalid data received');
return num;
}
async coinPrice(symbol) {
// Only common coins
const COINS = {
BCH: { decimals: 8, contract: '0x9f0f69428f923d6c95b781f89e165c9b2df9789d' },
BTC: { decimals: 8, contract: '0xf4030086522a5beea4988f8ca5b36dbc97bee88c' },
DOGE: { decimals: 8, contract: '0x2465cefd3b488be410b941b1d4b2767088e2a028' },
ETH: { decimals: 8, contract: '0x5f4ec3df9cbd43714fe2740f5e3616155c5b8419' },
XMR: { decimals: 8, contract: '0xfa66458cce7dd15d8650015c4fce4d278271618f' },
ZEC: { decimals: 8, contract: '0xd54b033d48d0475f19c5fccf7484e8a981848501' },
};
const coin = COINS[symbol.toUpperCase()];
if (!coin)
throw new Error(`micro-web3/chainlink: unknown coin: ${symbol}`);
return await this.price(coin.contract, coin.decimals);
}
async tokenPrice(symbol) {
const token = TOKENS[symbol.toUpperCase()];
if (!token)
throw new Error(`micro-web3/chainlink: unknown token: ${symbol}`);
return await this.price(token.contract, token.decimals);
}
}
//# sourceMappingURL=chainlink.js.map

File diff suppressed because one or more lines are too long

44
dev/env/node_modules/micro-eth-signer/esm/net/ens.d.ts generated vendored Executable file
View File

@@ -0,0 +1,44 @@
import { type IWeb3Provider } from '../utils.ts';
export declare function namehash(address: string): Uint8Array;
export default class ENS {
static ADDRESS_ZERO: string;
static REGISTRY: string;
static REGISTRY_CONTRACT: readonly [{
readonly name: "resolver";
readonly type: "function";
readonly inputs: readonly [{
readonly name: "node";
readonly type: "bytes32";
}];
readonly outputs: readonly [{
readonly type: "address";
}];
}];
static RESOLVER_CONTRACT: readonly [{
readonly name: "addr";
readonly type: "function";
readonly inputs: readonly [{
readonly name: "node";
readonly type: "bytes32";
}];
readonly outputs: readonly [{
readonly type: "address";
}];
}, {
readonly name: "name";
readonly type: "function";
readonly inputs: readonly [{
readonly name: "node";
readonly type: "bytes32";
}];
readonly outputs: readonly [{
readonly type: "string";
}];
}];
readonly net: IWeb3Provider;
constructor(net: IWeb3Provider);
getResolver(name: string): Promise<string | undefined>;
nameToAddress(name: string): Promise<string | undefined>;
addressToName(address: string): Promise<string | undefined>;
}
//# sourceMappingURL=ens.d.ts.map

1
dev/env/node_modules/micro-eth-signer/esm/net/ens.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"ens.d.ts","sourceRoot":"","sources":["../../src/net/ens.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,aAAa,EAAW,MAAM,aAAa,CAAC;AAG1D,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAMpD;AAED,MAAM,CAAC,OAAO,OAAO,GAAG;IACtB,MAAM,CAAC,YAAY,SAAgD;IACnE,MAAM,CAAC,QAAQ,SAAgD;IAC/D,MAAM,CAAC,iBAAiB;;;;;;;;;;OAOb;IACX,MAAM,CAAC,iBAAiB;;;;;;;;;;;;;;;;;;;;OAab;IAEX,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;gBAChB,GAAG,EAAE,aAAa;IAGxB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAOtD,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IASxD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CAelE"}

79
dev/env/node_modules/micro-eth-signer/esm/net/ens.js generated vendored Executable file
View File

@@ -0,0 +1,79 @@
import { keccak_256 } from '@noble/hashes/sha3';
import { concatBytes } from '@noble/hashes/utils';
import { createContract } from "../abi/decoder.js";
import { strip0x } from "../utils.js";
// No support for IDN names
export function namehash(address) {
let res = new Uint8Array(32);
if (!address)
return res;
for (let label of address.split('.').reverse())
res = keccak_256(concatBytes(res, keccak_256(label)));
return res;
}
class ENS {
constructor(net) {
this.net = net;
}
async getResolver(name) {
const contract = createContract(ENS.REGISTRY_CONTRACT, this.net, ENS.REGISTRY);
const res = await contract.resolver.call(namehash(name));
if (res === ENS.ADDRESS_ZERO)
return;
return res;
}
async nameToAddress(name) {
const resolver = await this.getResolver(name);
if (!resolver)
return;
const contract = createContract(ENS.RESOLVER_CONTRACT, this.net, resolver);
const addr = await contract.addr.call(namehash(name));
if (addr === ENS.ADDRESS_ZERO)
return;
return addr;
}
async addressToName(address) {
const addrDomain = `${strip0x(address).toLowerCase()}.addr.reverse`;
const resolver = await this.getResolver(addrDomain);
if (!resolver)
return;
const contract = createContract(ENS.RESOLVER_CONTRACT, this.net, resolver);
const name = await contract.name.call(namehash(addrDomain));
if (!name)
return;
// From spec: ENS does not enforce accuracy of reverse records -
// anyone may claim that the name for their address is 'alice.eth'.
// To be certain the claim is accurate, you must always perform a forward
// resolution for the returned name and check whether it matches the original address.
const realAddr = await this.nameToAddress(name);
if (realAddr !== address)
return;
return name;
}
}
ENS.ADDRESS_ZERO = '0x0000000000000000000000000000000000000000';
ENS.REGISTRY = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e';
ENS.REGISTRY_CONTRACT = [
{
name: 'resolver',
type: 'function',
inputs: [{ name: 'node', type: 'bytes32' }],
outputs: [{ type: 'address' }],
},
];
ENS.RESOLVER_CONTRACT = [
{
name: 'addr',
type: 'function',
inputs: [{ name: 'node', type: 'bytes32' }],
outputs: [{ type: 'address' }],
},
{
name: 'name',
type: 'function',
inputs: [{ name: 'node', type: 'bytes32' }],
outputs: [{ type: 'string' }],
},
];
export default ENS;
//# sourceMappingURL=ens.js.map

1
dev/env/node_modules/micro-eth-signer/esm/net/ens.js.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"ens.js","sourceRoot":"","sources":["../../src/net/ens.ts"],"names":[],"mappings":"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"}

7
dev/env/node_modules/micro-eth-signer/esm/net/index.d.ts generated vendored Executable file
View File

@@ -0,0 +1,7 @@
import { Web3Provider, calcTransfersDiff } from './archive.ts';
import Chainlink from './chainlink.ts';
import ENS from './ens.ts';
import UniswapV2 from './uniswap-v2.ts';
import UniswapV3 from './uniswap-v3.ts';
export { Chainlink, ENS, UniswapV2, UniswapV3, Web3Provider, calcTransfersDiff };
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/net/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,SAAS,MAAM,iBAAiB,CAAC;AACxC,OAAO,SAAS,MAAM,iBAAiB,CAAC;AAGxC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,iBAAiB,EAAE,CAAC"}

8
dev/env/node_modules/micro-eth-signer/esm/net/index.js generated vendored Executable file
View File

@@ -0,0 +1,8 @@
import { Web3Provider, calcTransfersDiff } from "./archive.js";
import Chainlink from "./chainlink.js";
import ENS from "./ens.js";
import UniswapV2 from "./uniswap-v2.js";
import UniswapV3 from "./uniswap-v3.js";
// There are many low level APIs inside which are not exported yet.
export { Chainlink, ENS, UniswapV2, UniswapV3, Web3Provider, calcTransfersDiff };
//# sourceMappingURL=index.js.map

1
dev/env/node_modules/micro-eth-signer/esm/net/index.js.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/net/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,SAAS,MAAM,iBAAiB,CAAC;AACxC,OAAO,SAAS,MAAM,iBAAiB,CAAC;AAExC,mEAAmE;AACnE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,iBAAiB,EAAE,CAAC"}

View File

@@ -0,0 +1,72 @@
import { type IWeb3Provider } from '../utils.ts';
export type SwapOpt = {
slippagePercent: number;
ttl: number;
};
export declare const DEFAULT_SWAP_OPT: SwapOpt;
export type ExchangeTx = {
address: string;
amount: string;
currency: string;
expectedAmount: string;
data?: string;
allowance?: {
token: string;
contract: string;
amount: string;
};
txId?: string;
};
export type SwapElm = {
name: string;
expectedAmount: string;
tx: (fromAddress: string, toAddress: string) => Promise<ExchangeTx>;
};
export declare function addPercent(n: bigint, _perc: number): bigint;
export declare function isPromise(o: unknown): boolean;
export type UnPromise<T> = T extends Promise<infer U> ? U : T;
type NestedUnPromise<T> = {
[K in keyof T]: NestedUnPromise<UnPromise<T[K]>>;
};
type UnPromiseIgnore<T> = T extends Promise<infer U> ? U | undefined : T;
type NestedUnPromiseIgnore<T> = {
[K in keyof T]: NestedUnPromiseIgnore<UnPromiseIgnore<T[K]>>;
};
export declare function awaitDeep<T, E extends boolean | undefined>(o: T, ignore_errors: E): Promise<E extends true ? NestedUnPromiseIgnore<T> : NestedUnPromise<T>>;
export type CommonBase = {
contract: string;
} & import('../abi/decoder.js').ContractInfo;
export declare const COMMON_BASES: CommonBase[];
export declare const WETH: string;
export declare function wrapContract(contract: string): string;
export declare function sortTokens(a: string, b: string): [string, string];
export declare function isValidEthAddr(address: string): boolean;
export declare function isValidUniAddr(address: string): boolean;
export type Token = {
decimals: number;
contract: string;
symbol: string;
};
export declare abstract class UniswapAbstract {
abstract name: string;
abstract contract: string;
abstract bestPath(fromCoin: string, toCoin: string, inputAmount: bigint): any;
abstract txData(toAddress: string, fromCoin: string, toCoin: string, path: any, inputAmount?: bigint, outputAmount?: bigint, opt?: {
slippagePercent: number;
}): any;
readonly net: IWeb3Provider;
constructor(net: IWeb3Provider);
swap(fromCoin: 'eth' | Token, toCoin: 'eth' | Token, amount: string, opt?: SwapOpt): Promise<{
name: string;
expectedAmount: string;
tx: (_fromAddress: string, toAddress: string) => Promise<{
amount: string;
address: any;
expectedAmount: string;
data: string;
allowance: any;
}>;
} | undefined>;
}
export {};
//# sourceMappingURL=uniswap-common.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"uniswap-common.d.ts","sourceRoot":"","sources":["../../src/net/uniswap-common.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,143 @@
import { tokenFromSymbol } from "../abi/index.js";
import { addr } from "../index.js";
import { createDecimal, ethHex, isBytes, weieth } from "../utils.js";
export const DEFAULT_SWAP_OPT = { slippagePercent: 0.5, ttl: 30 * 60 };
export function addPercent(n, _perc) {
const perc = BigInt((_perc * 10000) | 0);
const p100 = BigInt(100) * BigInt(10000);
return ((p100 + perc) * n) / p100;
}
export function isPromise(o) {
if (!o || !['object', 'function'].includes(typeof o))
return false;
return typeof o.then === 'function';
}
export async function awaitDeep(o, ignore_errors) {
let promises = [];
const traverse = (o) => {
if (Array.isArray(o))
return o.map((i) => traverse(i));
if (isBytes(o))
return o;
if (isPromise(o))
return { awaitDeep: promises.push(o) };
if (typeof o === 'object') {
let ret = {};
for (let k in o)
ret[k] = traverse(o[k]);
return ret;
}
return o;
};
let out = traverse(o);
let values;
if (!ignore_errors)
values = await Promise.all(promises);
else {
values = (await Promise.allSettled(promises)).map((i) => i.status === 'fulfilled' ? i.value : undefined);
}
const trBack = (o) => {
if (Array.isArray(o))
return o.map((i) => trBack(i));
if (isBytes(o))
return o;
if (typeof o === 'object') {
if (typeof o === 'object' && o.awaitDeep)
return values[o.awaitDeep - 1];
let ret = {};
for (let k in o)
ret[k] = trBack(o[k]);
return ret;
}
return o;
};
return trBack(out);
}
export const COMMON_BASES = [
'WETH',
'DAI',
'USDC',
'USDT',
'COMP',
'MKR',
'WBTC',
'AMPL',
]
.map((i) => tokenFromSymbol(i))
.filter((i) => !!i);
export const WETH = tokenFromSymbol('WETH').contract;
if (!WETH)
throw new Error('WETH is undefined!');
export function wrapContract(contract) {
contract = contract.toLowerCase();
return contract === 'eth' ? WETH : contract;
}
export function sortTokens(a, b) {
a = wrapContract(a);
b = wrapContract(b);
if (a === b)
throw new Error('uniswap.sortTokens: same token!');
return a < b ? [a, b] : [b, a];
}
export function isValidEthAddr(address) {
return addr.isValid(address);
}
export function isValidUniAddr(address) {
return address === 'eth' || isValidEthAddr(address);
}
function getToken(token) {
if (typeof token === 'string' && token.toLowerCase() === 'eth')
return { symbol: 'ETH', decimals: 18, contract: 'eth' };
return token;
}
export class UniswapAbstract {
constructor(net) {
this.net = net;
}
// private async coinInfo(netName: string) {
// if (!validateAddr(netName)) return;
// if (netName === 'eth') return { symbol: 'ETH', decimals: 18 };
// //return await this.mgr.tokenInfo('eth', netName);
// }
async swap(fromCoin, toCoin, amount, opt = DEFAULT_SWAP_OPT) {
const fromInfo = getToken(fromCoin);
const toInfo = getToken(toCoin);
if (!fromInfo || !toInfo)
return;
const fromContract = fromInfo.contract.toLowerCase();
const toContract = toInfo.contract.toLowerCase();
if (!fromContract || !toContract)
return;
const fromDecimal = createDecimal(fromInfo.decimals);
const toDecimal = createDecimal(toInfo.decimals);
const inputAmount = fromDecimal.decode(amount);
try {
const path = await this.bestPath(fromContract, toContract, inputAmount);
const expectedAmount = toDecimal.encode(path.amountOut);
return {
name: this.name,
expectedAmount,
tx: async (_fromAddress, toAddress) => {
const txUni = this.txData(toAddress, fromContract, toContract, path, inputAmount, undefined, opt);
return {
amount: weieth.encode(txUni.value),
address: txUni.to,
expectedAmount,
data: ethHex.encode(txUni.data),
allowance: txUni.allowance && {
token: txUni.allowance.token,
contract: this.contract,
amount: fromDecimal.encode(txUni.allowance.amount),
},
};
},
};
}
catch (e) {
// @ts-ignore
console.log('E', e);
return;
}
}
}
//# sourceMappingURL=uniswap-common.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,30 @@
import * as uni from './uniswap-common.ts';
export declare function create2(from: Uint8Array, salt: Uint8Array, initCodeHash: Uint8Array): string;
export declare function pairAddress(a: string, b: string, factory?: string): string;
export declare function amount(reserveIn: bigint, reserveOut: bigint, amountIn?: bigint, amountOut?: bigint): bigint;
export type Path = {
path: string[];
amountIn: bigint;
amountOut: bigint;
};
export declare function txData(to: string, input: string, output: string, path: Path, amountIn?: bigint, amountOut?: bigint, opt?: {
ttl: number;
deadline?: number;
slippagePercent: number;
feeOnTransfer: boolean;
}): {
to: string;
value: bigint;
data: any;
allowance: {
token: string;
amount: bigint;
} | undefined;
};
export default class UniswapV2 extends uni.UniswapAbstract {
name: string;
contract: string;
bestPath(fromCoin: string, toCoin: string, inputAmount: bigint): Promise<Path>;
txData(toAddress: string, fromCoin: string, toCoin: string, path: any, inputAmount?: bigint, outputAmount?: bigint, opt?: uni.SwapOpt): any;
}
//# sourceMappingURL=uniswap-v2.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"uniswap-v2.d.ts","sourceRoot":"","sources":["../../src/net/uniswap-v2.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAkB3C,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,GAAG,MAAM,CAG5F;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,MAAwB,GAAG,MAAM,CAI3F;AAaD,wBAAgB,MAAM,CACpB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAeR;AAED,MAAM,MAAM,IAAI,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AA8D3E,wBAAgB,MAAM,CACpB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,IAAI,EACV,QAAQ,CAAC,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,EAClB,GAAG,GAAE;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;CACP,GACjB;IACD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,GAAG,CAAC;IACV,SAAS,EACL;QACE,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,GACD,SAAS,CAAC;CACf,CAqCA;AAGD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,GAAG,CAAC,eAAe;IACxD,IAAI,SAAgB;IACpB,QAAQ,EAAE,MAAM,CAA8B;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAG9E,MAAM,CACJ,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,GAAG,EACT,WAAW,CAAC,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,MAAM,EACrB,GAAG,GAAE,GAAG,CAAC,OAA8B,GACtC,GAAG;CAMP"}

162
dev/env/node_modules/micro-eth-signer/esm/net/uniswap-v2.js generated vendored Executable file
View File

@@ -0,0 +1,162 @@
import { keccak_256 } from '@noble/hashes/sha3';
import { concatBytes, hexToBytes } from '@noble/hashes/utils';
import { createContract } from "../abi/decoder.js";
import { default as UNISWAP_V2_ROUTER, UNISWAP_V2_ROUTER_CONTRACT } from "../abi/uniswap-v2.js";
import { ethHex } from "../utils.js";
import * as uni from "./uniswap-common.js";
const FACTORY_ADDRESS = '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f';
const INIT_CODE_HASH = hexToBytes('96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f');
const PAIR_CONTRACT = [
{
type: 'function',
name: 'getReserves',
outputs: [
{ name: 'reserve0', type: 'uint112' },
{ name: 'reserve1', type: 'uint112' },
{ name: 'blockTimestampLast', type: 'uint32' },
],
},
];
export function create2(from, salt, initCodeHash) {
const cat = concatBytes(new Uint8Array([255]), from, salt, initCodeHash);
return ethHex.encode(keccak_256(cat).slice(12));
}
export function pairAddress(a, b, factory = FACTORY_ADDRESS) {
// This is completely broken: '0x11' '0x11' will return '0x1111'. But this is how it works in sdk.
const data = concatBytes(...uni.sortTokens(a, b).map((i) => ethHex.decode(i)));
return create2(ethHex.decode(factory), keccak_256(data), INIT_CODE_HASH);
}
async function reserves(net, a, b) {
a = uni.wrapContract(a);
b = uni.wrapContract(b);
const contract = createContract(PAIR_CONTRACT, net, pairAddress(a, b));
const res = await contract.getReserves.call();
return a < b ? [res.reserve0, res.reserve1] : [res.reserve1, res.reserve0];
}
// amountIn set: returns amountOut, how many tokenB user gets for amountIn of tokenA
// amountOut set: returns amountIn, how many tokenA user should send to get exact
// amountOut of tokenB
export function amount(reserveIn, reserveOut, amountIn, amountOut) {
if (amountIn && amountOut)
throw new Error('uniswap.amount: provide only one amount');
if (!reserveIn || !reserveOut || (amountOut && amountOut >= reserveOut))
throw new Error('Uniswap: Insufficient reserves');
if (amountIn) {
const amountInWithFee = amountIn * BigInt(997);
const amountOut = (amountInWithFee * reserveOut) / (reserveIn * BigInt(1000) + amountInWithFee);
if (amountOut === BigInt(0) || amountOut >= reserveOut)
throw new Error('Uniswap: Insufficient reserves');
return amountOut;
}
else if (amountOut)
return ((reserveIn * amountOut * BigInt(1000)) / ((reserveOut - amountOut) * BigInt(997)) + BigInt(1));
else
throw new Error('uniswap.amount: provide only one amount');
}
async function bestPath(net, tokenA, tokenB, amountIn, amountOut) {
if ((amountIn && amountOut) || (!amountIn && !amountOut))
throw new Error('uniswap.bestPath: provide only one amount');
const wA = uni.wrapContract(tokenA);
const wB = uni.wrapContract(tokenB);
let resP = [];
// Direct pair
resP.push((async () => {
const pairAmount = amount(...(await reserves(net, tokenA, tokenB)), amountIn, amountOut);
return {
path: [wA, wB],
amountIn: amountIn ? amountIn : pairAmount,
amountOut: amountOut ? amountOut : pairAmount,
};
})());
const BASES = uni.COMMON_BASES.filter((c) => c && c.contract && c.contract !== wA && c.contract !== wB);
for (let c of BASES) {
resP.push((async () => {
const [rAC, rCB] = await Promise.all([
reserves(net, wA, c.contract),
reserves(net, c.contract, wB),
]);
const path = [wA, c.contract, wB];
if (amountIn)
return { path, amountIn, amountOut: amount(...rCB, amount(...rAC, amountIn)) };
else if (amountOut) {
return {
path,
amountOut,
amountIn: amount(...rAC, undefined, amount(...rCB, undefined, amountOut)),
};
}
else
throw new Error('Impossible invariant');
})());
}
let res = (await uni.awaitDeep(resP, true)).filter((i) => !!i);
// biggest output or smallest input
res.sort((a, b) => Number(amountIn ? b.amountOut - a.amountOut : a.amountIn - b.amountIn));
if (!res.length)
throw new Error('uniswap: cannot find path');
return res[0];
}
const ROUTER_CONTRACT = createContract(UNISWAP_V2_ROUTER, undefined, UNISWAP_V2_ROUTER_CONTRACT);
const TX_DEFAULT_OPT = {
...uni.DEFAULT_SWAP_OPT,
feeOnTransfer: false, // have no idea what it is
};
export function txData(to, input, output, path, amountIn, amountOut, opt = TX_DEFAULT_OPT) {
opt = { ...TX_DEFAULT_OPT, ...opt };
if (!uni.isValidUniAddr(input) || !uni.isValidUniAddr(output) || !uni.isValidEthAddr(to))
throw new Error('Invalid address');
if (input === 'eth' && output === 'eth')
throw new Error('Both input and output is ETH!');
if (input === 'eth' && path.path[0] !== uni.WETH)
throw new Error('Input is ETH but path starts with different contract');
if (output === 'eth' && path.path[path.path.length - 1] !== uni.WETH)
throw new Error('Output is ETH but path ends with different contract');
if ((amountIn && amountOut) || (!amountIn && !amountOut))
throw new Error('uniswap.txData: provide only one amount');
if (amountOut && opt.feeOnTransfer)
throw new Error('Exact output + feeOnTransfer is impossible');
const method = ('swap' +
(amountIn ? 'Exact' : '') +
(input === 'eth' ? 'ETH' : 'Tokens') +
'For' +
(amountOut ? 'Exact' : '') +
(output === 'eth' ? 'ETH' : 'Tokens') +
(opt.feeOnTransfer ? 'SupportingFeeOnTransferTokens' : ''));
if (!(method in ROUTER_CONTRACT))
throw new Error('Invalid method');
const deadline = opt.deadline ? opt.deadline : Math.floor(Date.now() / 1000) + opt.ttl;
const amountInMax = uni.addPercent(path.amountIn, opt.slippagePercent);
const amountOutMin = uni.addPercent(path.amountOut, -opt.slippagePercent);
// TODO: remove any
const data = ROUTER_CONTRACT[method].encodeInput({
amountInMax,
amountOutMin,
amountIn,
amountOut,
to,
deadline,
path: path.path,
});
const amount = amountIn ? amountIn : amountInMax;
const value = input === 'eth' ? amount : BigInt(0);
const allowance = input === 'eth' ? undefined : { token: input, amount };
return { to: UNISWAP_V2_ROUTER_CONTRACT, value, data, allowance };
}
// Here goes Exchange API. Everything above is SDK. Supports almost everything from official sdk except liquidity stuff.
export default class UniswapV2 extends uni.UniswapAbstract {
constructor() {
super(...arguments);
this.name = 'Uniswap V2';
this.contract = UNISWAP_V2_ROUTER_CONTRACT;
}
bestPath(fromCoin, toCoin, inputAmount) {
return bestPath(this.net, fromCoin, toCoin, inputAmount);
}
txData(toAddress, fromCoin, toCoin, path, inputAmount, outputAmount, opt = uni.DEFAULT_SWAP_OPT) {
return txData(toAddress, fromCoin, toCoin, path, inputAmount, outputAmount, {
...TX_DEFAULT_OPT,
...opt,
});
}
}
//# sourceMappingURL=uniswap-v2.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,36 @@
import * as uni from './uniswap-common.ts';
export declare const Fee: Record<string, number>;
type Route = {
path?: Uint8Array;
fee?: number;
amountIn?: bigint;
amountOut?: bigint;
p?: any;
};
export type TxOpt = {
slippagePercent: number;
ttl: number;
sqrtPriceLimitX96?: bigint;
deadline?: number;
fee?: {
fee: number;
to: string;
};
};
export declare function txData(to: string, input: string, output: string, route: Route, amountIn?: bigint, amountOut?: bigint, opt?: TxOpt): {
to: string;
value: bigint;
data: Uint8Array;
allowance: {
token: string;
amount: bigint;
} | undefined;
};
export default class UniswapV3 extends uni.UniswapAbstract {
name: string;
contract: string;
bestPath(fromCoin: string, toCoin: string, inputAmount: bigint): Promise<Route>;
txData(toAddress: string, fromCoin: string, toCoin: string, path: any, inputAmount?: bigint, outputAmount?: bigint, opt?: uni.SwapOpt): any;
}
export {};
//# sourceMappingURL=uniswap-v3.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"uniswap-v3.d.ts","sourceRoot":"","sources":["../../src/net/uniswap-v3.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAiD3C,eAAO,MAAM,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAItC,CAAC;AAEF,KAAK,KAAK,GAAG;IAAE,IAAI,CAAC,EAAE,UAAU,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC;AAoDjG,MAAM,MAAM,KAAK,GAAG;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;CACnC,CAAC;AAEF,wBAAgB,MAAM,CACpB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,KAAK,EACZ,QAAQ,CAAC,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,EAClB,GAAG,GAAE,KAA4B,GAChC;IACD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EACL;QACE,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,GACD,SAAS,CAAC;CACf,CAoEA;AAGD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,GAAG,CAAC,eAAe;IACxD,IAAI,SAAgB;IACpB,QAAQ,EAAE,MAAM,CAA8B;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAG/E,MAAM,CACJ,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,GAAG,EACT,WAAW,CAAC,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,MAAM,EACrB,GAAG,GAAE,GAAG,CAAC,OAA8B,GACtC,GAAG;CAMP"}

176
dev/env/node_modules/micro-eth-signer/esm/net/uniswap-v3.js generated vendored Executable file
View File

@@ -0,0 +1,176 @@
import { concatBytes } from '@noble/hashes/utils';
import { createContract } from "../abi/decoder.js";
import { default as UNISWAP_V3_ROUTER, UNISWAP_V3_ROUTER_CONTRACT } from "../abi/uniswap-v3.js";
import { ethHex } from "../utils.js";
import * as uni from "./uniswap-common.js";
const ADDRESS_ZERO = '0x0000000000000000000000000000000000000000';
const QUOTER_ADDRESS = '0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6';
const QUOTER_ABI = [
{
type: 'function',
name: 'quoteExactInput',
inputs: [
{ name: 'path', type: 'bytes' },
{ name: 'amountIn', type: 'uint256' },
],
outputs: [{ name: 'amountOut', type: 'uint256' }],
},
{
type: 'function',
name: 'quoteExactInputSingle',
inputs: [
{ name: 'tokenIn', type: 'address' },
{ name: 'tokenOut', type: 'address' },
{ name: 'fee', type: 'uint24' },
{ name: 'amountIn', type: 'uint256' },
{ name: 'sqrtPriceLimitX96', type: 'uint160' },
],
outputs: [{ name: 'amountOut', type: 'uint256' }],
},
{
type: 'function',
name: 'quoteExactOutput',
inputs: [
{ name: 'path', type: 'bytes' },
{ name: 'amountOut', type: 'uint256' },
],
outputs: [{ name: 'amountIn', type: 'uint256' }],
},
{
type: 'function',
name: 'quoteExactOutputSingle',
inputs: [
{ name: 'tokenIn', type: 'address' },
{ name: 'tokenOut', type: 'address' },
{ name: 'fee', type: 'uint24' },
{ name: 'amountOut', type: 'uint256' },
{ name: 'sqrtPriceLimitX96', type: 'uint160' },
],
outputs: [{ name: 'amountIn', type: 'uint256' }],
},
];
export const Fee = {
LOW: 500,
MEDIUM: 3000,
HIGH: 10000,
};
function basePaths(a, b, exactOutput = false) {
let res = [];
for (let fee in Fee)
res.push({ fee: Fee[fee], p: [a, b] });
const wA = uni.wrapContract(a);
const wB = uni.wrapContract(b);
const BASES = uni.COMMON_BASES.filter((c) => c && c.contract && c.contract !== wA && c.contract !== wB);
const packFee = (n) => Fee[n].toString(16).padStart(6, '0');
for (let c of BASES) {
for (let fee1 in Fee) {
for (let fee2 in Fee) {
let path = [wA, packFee(fee1), c.contract, packFee(fee2), wB].map((i) => ethHex.decode(i));
if (exactOutput)
path = path.reverse();
res.push({ path: concatBytes(...path) });
}
}
}
return res;
}
async function bestPath(net, a, b, amountIn, amountOut) {
if ((amountIn && amountOut) || (!amountIn && !amountOut))
throw new Error('uniswapV3.bestPath: provide only one amount');
const quoter = createContract(QUOTER_ABI, net, QUOTER_ADDRESS);
let paths = basePaths(a, b, !!amountOut);
for (let i of paths) {
if (!i.path && !i.fee)
continue;
const opt = { ...i, tokenIn: a, tokenOut: b, amountIn, amountOut, sqrtPriceLimitX96: 0 };
const method = 'quoteExact' + (amountIn ? 'Input' : 'Output') + (i.path ? '' : 'Single');
// TODO: remove any
i[amountIn ? 'amountOut' : 'amountIn'] = quoter[method].call(opt);
}
paths = (await uni.awaitDeep(paths, true));
paths = paths.filter((i) => i.amountIn || i.amountOut);
paths.sort((a, b) => Number(amountIn ? b.amountOut - a.amountOut : a.amountIn - b.amountIn));
if (!paths.length)
throw new Error('uniswap: cannot find path');
return paths[0];
}
const ROUTER_CONTRACT = createContract(UNISWAP_V3_ROUTER, undefined, UNISWAP_V3_ROUTER_CONTRACT);
export function txData(to, input, output, route, amountIn, amountOut, opt = uni.DEFAULT_SWAP_OPT) {
opt = { ...uni.DEFAULT_SWAP_OPT, ...opt };
const err = 'Uniswap v3: ';
if (!uni.isValidUniAddr(input))
throw new Error(err + 'invalid input address');
if (!uni.isValidUniAddr(output))
throw new Error(err + 'invalid output address');
if (!uni.isValidEthAddr(to))
throw new Error(err + 'invalid to address');
if (opt.fee && !uni.isValidUniAddr(opt.fee.to))
throw new Error(err + 'invalid fee recepient addresss');
if (input === 'eth' && output === 'eth')
throw new Error(err + 'both input and output cannot be eth');
if ((amountIn && amountOut) || (!amountIn && !amountOut))
throw new Error(err + 'specify either amountIn or amountOut, but not both');
if ((amountIn && !route.amountOut) ||
(amountOut && !route.amountIn) ||
(!route.fee && !route.path))
throw new Error(err + 'invalid route');
if (route.path && opt.sqrtPriceLimitX96)
throw new Error(err + 'sqrtPriceLimitX96 on multi-hop trade');
const deadline = opt.deadline || Math.floor(Date.now() / 1000);
// flags for whether funds should be send first to the router
const routerMustCustody = output === 'eth' || !!opt.fee;
// TODO: remove "as bigint"
let args = {
...route,
tokenIn: uni.wrapContract(input),
tokenOut: uni.wrapContract(output),
recipient: routerMustCustody ? ADDRESS_ZERO : to,
deadline,
amountIn: (amountIn || route.amountIn),
amountOut: (amountOut || route.amountOut),
sqrtPriceLimitX96: opt.sqrtPriceLimitX96 || BigInt(0),
amountInMaximum: undefined,
amountOutMinimum: undefined,
};
args.amountInMaximum = uni.addPercent(args.amountIn, opt.slippagePercent);
args.amountOutMinimum = uni.addPercent(args.amountOut, -opt.slippagePercent);
const method = ('exact' + (amountIn ? 'Input' : 'Output') + (!args.path ? 'Single' : ''));
// TODO: remove unknown
const calldatas = [ROUTER_CONTRACT[method].encodeInput(args)];
if (input === 'eth' && amountOut)
calldatas.push(ROUTER_CONTRACT['refundETH'].encodeInput());
// unwrap
if (routerMustCustody) {
calldatas.push(ROUTER_CONTRACT[(output === 'eth' ? 'unwrapWETH9' : 'sweepToken') + (opt.fee ? 'WithFee' : '')].encodeInput({
token: uni.wrapContract(output),
amountMinimum: args.amountOutMinimum,
recipient: to,
feeBips: opt.fee && opt.fee.fee * 10000,
feeRecipient: opt.fee && opt.fee.to,
}));
}
const data = calldatas.length === 1 ? calldatas[0] : ROUTER_CONTRACT['multicall'].encodeInput(calldatas);
const value = input === 'eth' ? (amountIn ? amountIn : args.amountInMaximum) : BigInt(0);
const allowance = input !== 'eth'
? { token: input, amount: amountIn ? amountIn : args.amountInMaximum }
: undefined;
return { to: UNISWAP_V3_ROUTER_CONTRACT, value, data, allowance };
}
// Here goes Exchange API. Everything above is SDK.
export default class UniswapV3 extends uni.UniswapAbstract {
constructor() {
super(...arguments);
this.name = 'Uniswap V3';
this.contract = UNISWAP_V3_ROUTER_CONTRACT;
}
bestPath(fromCoin, toCoin, inputAmount) {
return bestPath(this.net, fromCoin, toCoin, inputAmount);
}
txData(toAddress, fromCoin, toCoin, path, inputAmount, outputAmount, opt = uni.DEFAULT_SWAP_OPT) {
return txData(toAddress, fromCoin, toCoin, path, inputAmount, outputAmount, {
...uni.DEFAULT_SWAP_OPT,
...opt,
});
}
}
//# sourceMappingURL=uniswap-v3.js.map

File diff suppressed because one or more lines are too long