All files / src/chain action.ts

94.28% Statements 33/35
90% Branches 18/20
100% Functions 4/4
94.28% Lines 33/35

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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146                                                                                            1x 1x   1x   1x   1x   1x         4853x 4853x 4853x   2365x 2345x 2345x 1x       20x 2x       2362x           4850x 4850x 2352x   2498x 2498x 2498x                         4850x         9x 9x                         4x 1x         3x 3x 3x     3x         2x     2x            
import {abiEncode} from '../serializer/encoder'
import {abiDecode} from '../serializer/decoder'
import {
    ABISerializable,
    ABISerializableConstructor,
    ABISerializableObject,
    ABISerializableType,
    synthesizeABI,
} from '../serializer/serializable'
 
import {arrayEquatableEquals} from '../utils'
import {BuiltinTypes, getType} from '../serializer/builtins'
 
import {
    ABI,
    ABIDef,
    Bytes,
    BytesType,
    Name,
    NameType,
    PermissionLevel,
    PermissionLevelType,
    Struct,
} from '../'
 
interface ActionBase {
    /** The account (a.k.a. contract) to run action on. */
    account: NameType
    /** The name of the action. */
    name: NameType
    /** The permissions authorizing the action. */
    authorization: PermissionLevelType[]
}
 
export interface ActionFields extends ActionBase {
    /** The ABI-encoded action data. */
    data: BytesType
}
 
/** Action type that may or may not have its data encoded */
export interface AnyAction extends ActionBase {
    data: BytesType | ABISerializableObject | Record<string, any>
}
 
export type ActionType = Action | ActionFields
 
@Struct.type('action')
export class Action extends Struct {
    /** The account (a.k.a. contract) to run action on. */
    @Struct.field('name') account!: Name
    /** The name of the action. */
    @Struct.field('name') name!: Name
    /** The permissions authorizing the action. */
    @Struct.field(PermissionLevel, {array: true}) authorization!: PermissionLevel[]
    /** The ABI-encoded action data. */
    @Struct.field('bytes') data!: Bytes
 
    public abi?: ABI
 
    static from(anyAction: ActionType | AnyAction, abi?: ABIDef): Action {
        let object = {...anyAction}
        const data = object.data as any
        if (!Bytes.isBytes(data)) {
            let type: string | undefined
            if (abi) {
                type = ABI.from(abi).getActionType(object.name)
                if (!type) {
                    throw new Error(
                        `The action "${object.name}" does not exist on the ABI provided.`
                    )
                }
            } else if (!data.constructor || data.constructor.abiName === undefined) {
                throw new Error(
                    'Missing ABI definition when creating action with untyped action data'
                )
            }
            object = {
                ...object,
                data: abiEncode({object: data, type, abi}),
            }
        }
 
        const action = super.from(object) as Action
        if (abi) {
            action.abi = ABI.from(abi)
        } else {
            const type = getType(data)
            if (type) {
                action.abi = ABI.from({
                    ...synthesizeABI(type).abi,
                    actions: [
                        {
                            name: action.name,
                            type: type.abiName,
                            ricardian_contract: '',
                        },
                    ],
                })
            }
        }
 
        return action
    }
 
    /** Return true if this Action is equal to given action. */
    equals(other: ActionType | AnyAction) {
        const otherAction = Action.from(other, this.abi)
        return (
            this.account.equals(otherAction.account) &&
            this.name.equals(otherAction.name) &&
            arrayEquatableEquals(this.authorization, otherAction.authorization) &&
            this.data.equals(otherAction.data)
        )
    }
 
    /** Return action data decoded as given type or using ABI. */
    decodeData<T extends ABISerializableConstructor>(type: T): InstanceType<T>
    decodeData<T extends keyof BuiltinTypes>(type: T): BuiltinTypes[T]
    decodeData(abi: ABIDef): ABISerializable
    decodeData(typeOrAbi: ABISerializableType | ABIDef) {
        if (typeof typeOrAbi === 'string' || (typeOrAbi as ABISerializableConstructor).abiName) {
            return abiDecode({
                data: this.data,
                type: typeOrAbi as string,
            })
        } else {
            const abi = ABI.from(typeOrAbi as ABIDef)
            const type = abi.getActionType(this.name)
            Iif (!type) {
                throw new Error(`Action ${this.name} does not exist in provided ABI`)
            }
            return abiDecode({data: this.data, type, abi})
        }
    }
 
    get decoded() {
        Iif (!this.abi) {
            throw new Error('Missing ABI definition when decoding action data')
        }
        return {
            ...this.toJSON(),
            data: this.decodeData(this.abi),
        }
    }
}