export function mixin(child, ...parents) { // child is modified directly, assigning fields from parents that are missing in child. parents fields are // assigned by parents order, highest priority first for( const parent of parents ) { for ( const key in parent) { if (parent.hasOwnProperty(key) && !child.hasOwnProperty(key) && parent[key] !== undefined) { child[key] = parent[key]; } } } return child; } export function prototype(parent, child) { const result = Object.create(parent); Object.assign(result, child) return result } export function invokeCallback(cbs, prop, ...args) { if (prop in cbs) cbs[prop].call(cbs, ...args) } export function invokeCallbacks(callbacks, prop, ...args) { if (!callbacks) return callbacks.forEach((cb)=>invokeCallback(cb, prop, ...args)) } export function encodeIEE754(value) { const buffer = new ArrayBuffer(4); const view = new DataView(buffer); view.setFloat32(0, value, false /* big endian */); return view.getUint32(0, false); } export function decodeIEE754(value) { const buffer = new ArrayBuffer(4); const view = new DataView(buffer); view.setUint32(0, value, false) return view.getFloat32(0, false) } export function buildMetadataMap(metadata) { const metadataMap = {} for (const [chain, info] of Object.entries(metadata)) { const map = {} for (const poolMeta of info.p) map[poolMeta.a] = poolMeta for (const tokenMeta of info.t) map[tokenMeta.a] = tokenMeta metadataMap[Number(chain)] = map } // console.log('metadataMap', metadataMap) return metadataMap } // // AsyncCache // export class AsyncCache { // fetch(key) returns a value constructor(fetch) { this.cache = {} this.fetchLocks = {} this.fetch = fetch } async get(key) { if (this.cache[key]) { return this.cache[key] } if (this.fetchLocks[key]) { return await this.fetchLocks[key] } const fetchPromise = this.fetch(key) this.fetchLocks[key] = fetchPromise const result = await fetchPromise this.cache[key] = result delete this.fetchLocks[key] return result } } export class AsyncAbiCache extends AsyncCache { constructor(fetch) { super(async (key)=>{ const result = await fetch(key) return result.abi }); } } export class AsyncURLCache extends AsyncAbiCache { constructor(urlForKey) { super(async (key) => { const URL = this.urlForKey(key) const response = await fetch(URL) if (!response.ok) throw new Error(`Could not fetch ${URL} (status ${response.status})`) return await response.json() }) this.urlForKey = urlForKey } } export class AbiURLCache extends AsyncURLCache { constructor(baseUrl) { super((name)=>{ return this.baseUrl+abiPath(name) }) this.baseUrl = baseUrl.endsWith('/') ? baseUrl : baseUrl + '/' } } const files = { // If a contract is in a file different than its name, put the exception here // 'IVaultImpl' : 'IVault', // for example // 'IERC20Metadata' : 'interfaces/IERC20Metadata', } export function abiPath(name) { const file = files[name] return `${file??name}.sol/${name}.json` } export function parseFill(obj) { let [tx, time, filledIn, filledOut, fee] = obj time = new Date(time * 1000) filledIn = BigInt(filledIn) filledOut = BigInt(filledOut) fee = BigInt(fee) return {tx, time, filledIn, filledOut, fee} } export function timestamp(date = null) { if (date === null) date = new Date() return Math.round(date.getTime() / 1000) } export function dateString(datetime) { return datetime.toLocaleString({dateStyle: 'medium', timeStyle: 'short'}) }