All files abi.ts

90.47% Statements 38/42
77.77% Branches 21/27
100% Functions 9/9
90% Lines 36/40

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 9155x                         1x 15x 15x   15x   15x     13x 13x 13x 4x   9x 9x 9x 9x   9x   9x 8x 8x 8x 8x 8x 8x                 8x 8x 8x 8x 6x   2x 2x 1x   1x           7x                                 2x 49x 23204x     641x      
import {ABI, ABIDef, API, APIClient, NameType} from '@wharfkit/antelope'
import {AbiProvider} from '@wharfkit/signing-request'
 
export interface ABICacheInterface extends AbiProvider {
    readonly cache: Map<string, ABI>
    readonly pending: Map<string, Promise<API.v1.GetRawAbiResponse>>
    getAbi(account: NameType): Promise<ABI>
    setAbi(account: NameType, abi: ABIDef, merge?: boolean): void
}
 
/**
 * Given an APIClient instance, this class provides an AbiProvider interface for retrieving and caching ABIs.
 */
export class ABICache implements ABICacheInterface {
    readonly cache: Map<string, ABI> = new Map()
    readonly pending: Map<string, Promise<API.v1.GetRawAbiResponse>> = new Map()
    // Keys whose cached ABI came only from merged partial (action-synthesized) ABIs and is not yet reconciled with chain.
    readonly partial: Set<string> = new Set()
 
    constructor(readonly client: APIClient) {}
 
    async getAbi(account: NameType): Promise<ABI> {
        const key = String(account)
        const record = this.cache.get(key)
        if (record && !this.partial.has(key)) {
            return record
        }
        let getAbi = this.pending.get(key)
        if (!getAbi) {
            getAbi = this.client.v1.chain.get_raw_abi(account).finally(() => {
                this.pending.delete(key)
            })
            this.pending.set(key, getAbi)
        }
        const response = await getAbi
        if (response.abi) {
            const chainAbi = ABI.from(response.abi)
            const resolved = record ? ABICache.merge(chainAbi, record, chainAbi.version) : chainAbi
            this.cache.set(key, resolved)
            this.partial.delete(key)
            return resolved
        E} else if (record) {
            return record
        } else {
            throw new Error(`ABI for ${key} could not be loaded.`)
        }
    }
 
    setAbi(account: NameType, abiDef: ABIDef, merge = false) {
        const key = String(account)
        const abi = ABI.from(abiDef)
        const existing = this.cache.get(key)
        if (merge && existing) {
            this.cache.set(key, ABICache.merge(existing, abi, abi.version))
        } else {
            this.cache.set(key, abi)
            if (merge) {
                this.partial.add(key)
            } else {
                this.partial.delete(key)
            }
        }
    }
 
    private static merge(base: ABI, addition: ABI, version: string): ABI {
        return ABI.from({
            action_results: mergeAndDeduplicate(base.action_results, addition.action_results),
            types: mergeAndDeduplicate(base.types, addition.types, 'new_type_name'),
            structs: mergeAndDeduplicate(base.structs, addition.structs),
            actions: mergeAndDeduplicate(base.actions, addition.actions),
            tables: mergeAndDeduplicate(base.tables, addition.tables),
            ricardian_clauses: mergeAndDeduplicate(
                base.ricardian_clauses,
                addition.ricardian_clauses,
                'id'
            ),
            variants: mergeAndDeduplicate(base.variants, addition.variants),
            version,
        })
    }
}
 
function mergeAndDeduplicate(array1, array2, byField = 'name') {
    return array2.reduce((acc: any[], current: any) => {
        Iif (!acc.some((obj: any) => String(obj[byField]) === String(current[byField]))) {
            acc.push(current)
        }
        return acc
    }, array1.slice())
}