UNPKG

@neo-one/smart-contract-compiler

Version:

NEO•ONE TypeScript smart contract compiler.

248 lines (246 loc) 11.8 kB
import { TransactionTypeModel as TransactionType } from '@neo-one/client-common'; import { utils } from '@neo-one/utils'; import _ from 'lodash'; import ts from 'typescript'; import { ContractPropertyName } from '../../../constants'; import { Helper } from '../Helper'; import { findDeployInfo } from './utils'; export class InvokeSmartContractHelper extends Helper { constructor({ contractInfo }) { super(); this.contractInfo = contractInfo; } emit(sb, node, optionsIn) { const options = sb.pushValueOptions(optionsIn); const getCaseBase = (decl, name, whenTrue) => ({ condition: () => { sb.emitOp(decl, 'DUP'); sb.emitPushString(decl, name); sb.emitOp(decl, 'EQUAL'); }, whenTrue: () => { sb.emitOp(decl, 'DROP'); whenTrue(); }, }); const getFunctionCase = (propInfo) => getCaseBase(propInfo.decl, propInfo.name, () => { const decl = propInfo.decl; if (ts.isPropertyDeclaration(decl)) { sb.context.reportUnsupported(decl); return; } if (propInfo.send) { sb.emitHelper(decl, options, sb.helpers.handleSend({ method: decl, returnType: propInfo.returnType })); } else if (propInfo.receive) { sb.emitHelper(decl, options, sb.helpers.handleReceive({ opposite: propInfo.sendUnsafe, method: decl, returnType: propInfo.returnType })); } else if (propInfo.sendUnsafe) { sb.emitHelper(decl, options, sb.helpers.handleSendUnsafe({ opposite: propInfo.receive, method: decl, returnType: propInfo.returnType })); } else { sb.emitHelper(decl, options, sb.helpers.handleNormal({ propInfo })); } }); const getDeployCase = (contractInfo, propInfo) => { const decl = propInfo.decl === undefined ? propInfo.classDecl : propInfo.decl; return getCaseBase(decl, propInfo.name, () => { sb.emitHelper(decl, options, sb.helpers.deploy({ contractInfo, propInfo })); }); }; const getPropertyCase = (propInfo) => { const decl = propInfo.decl; return getCaseBase(decl, propInfo.name, () => { sb.emitHelper(decl, options, sb.helpers.handleNormal({ propInfo })); }); }; const getAccessorCase = (propInfo) => { const mutableCases = []; const getter = propInfo.getter; if (getter !== undefined) { mutableCases.push(getCaseBase(getter.decl, getter.name, () => { sb.emitHelper(getter.decl, options, sb.helpers.handleNormal({ propInfo, getter: true })); })); } const setter = propInfo.setter; if (setter !== undefined) { mutableCases.push(getCaseBase(setter.decl, setter.name, () => { sb.emitHelper(setter.decl, options, sb.helpers.handleNormal({ propInfo, getter: false })); })); } return mutableCases; }; const getRefundAssetsCase = (propInfo) => getCaseBase(node, ContractPropertyName.refundAssets, () => { sb.emitHelper(node, options, sb.helpers.handleNormal({ propInfo })); }); const getCompleteSendCase = (propInfo) => getCaseBase(node, ContractPropertyName.completeSend, () => { sb.emitHelper(node, options, sb.helpers.handleNormal({ propInfo })); }); const getUpgradeCase = (propInfo) => getCaseBase(node, ContractPropertyName.upgrade, () => { sb.emitHelper(node, options, sb.helpers.handleNormal({ propInfo })); }); const allCases = _.flatten(this.contractInfo.propInfos .filter((propInfo) => propInfo.isPublic && propInfo.type !== 'deploy') .map((propInfo) => { if (propInfo.type === 'function') { return [ { propCase: getFunctionCase(propInfo), claimVerify: propInfo.claim, invokeVerify: propInfo.send || propInfo.sendUnsafe || propInfo.receive, }, ]; } if (propInfo.type === 'refundAssets') { return [{ propCase: getRefundAssetsCase(propInfo), claimVerify: false, invokeVerify: true }]; } if (propInfo.type === 'completeSend') { return [{ propCase: getCompleteSendCase(propInfo), claimVerify: false, invokeVerify: true }]; } if (propInfo.type === 'property') { return [{ propCase: getPropertyCase(propInfo), claimVerify: false, invokeVerify: false }]; } if (propInfo.type === 'accessor') { return getAccessorCase(propInfo).map((propCase) => ({ propCase, claimVerify: false, invokeVerify: false, })); } if (propInfo.type === 'upgrade') { return [{ propCase: getUpgradeCase(propInfo), claimVerify: false, invokeVerify: false }]; } if (propInfo.type === 'deploy') { throw new Error('For TS'); } utils.assertNever(propInfo); throw new Error('For TS'); })); let applicationCases = allCases.filter((propCase) => !propCase.claimVerify).map(({ propCase }) => propCase); const deploy = findDeployInfo(this.contractInfo); if (deploy !== undefined) { applicationCases = applicationCases.concat(getDeployCase(deploy[0], deploy[1])); } const invocationVerifyCases = allCases.filter((propCase) => propCase.invokeVerify).map(({ propCase }) => propCase); const nonVerifyCases = allCases.filter((propCase) => !propCase.invokeVerify).map(({ propCase }) => propCase); const claimCases = allCases.filter((propCase) => propCase.claimVerify).map(({ propCase }) => propCase); const throwDefault = () => { sb.emitOp(node, 'DROP'); sb.emitOp(node, 'THROW'); }; const handleInvokeVerify = (func) => { sb.emitHelper(node, options, sb.helpers.if({ condition: () => { sb.emitHelper(node, options, sb.helpers.applicationMatchesVerification); sb.emitOp(node, 'DUP'); }, whenTrue: () => { func(); sb.emitOp(node, 'DROP'); }, })); }; const handleClaimVerify = (func) => { func(); sb.emitOp(node, 'DROP'); sb.emitPushBoolean(node, true); }; const handleDefaultInvokeVerify = () => { sb.emitOp(node, 'DROP'); sb.emitHelper(node, options, sb.helpers.didReceiveAssets); sb.emitHelper(node, options, sb.helpers.didSendAssets); sb.emitHelper(node, options, sb.helpers.if({ condition: () => { sb.emitOp(node, 'BOOLOR'); }, whenTrue: () => { sb.emitOp(node, 'THROW'); }, whenFalse: () => { sb.emitPushInt(node, 0); sb.emitHelper(node, options, sb.helpers.getArgument); sb.emitHelper(node, options, sb.helpers.case(nonVerifyCases, throwDefault)); }, })); }; sb.emitSysCall(node, 'Neo.Runtime.GetTrigger'); sb.emitHelper(node, options, sb.helpers.case([ { condition: () => { sb.emitOp(node, 'DUP'); sb.emitPushInt(node, 0x10); sb.emitOp(node, 'NUMEQUAL'); }, whenTrue: () => { sb.emitOp(node, 'DROP'); sb.emitPushInt(node, 0); sb.emitHelper(node, options, sb.helpers.getArgument); sb.emitHelper(node, options, sb.helpers.case(applicationCases, throwDefault)); }, }, { condition: () => { sb.emitOp(node, 'DUP'); sb.emitPushInt(node, 0x00); sb.emitOp(node, 'NUMEQUAL'); }, whenTrue: () => { sb.emitOp(node, 'DROP'); sb.emitSysCall(node, 'System.ExecutionEngine.GetScriptContainer'); sb.emitSysCall(node, 'Neo.Transaction.GetType'); sb.emitHelper(node, options, sb.helpers.case([ { condition: () => { sb.emitOp(node, 'DUP'); sb.emitPushInt(node, TransactionType.Invocation); sb.emitOp(node, 'NUMEQUAL'); }, whenTrue: () => { sb.emitOp(node, 'DROP'); sb.emitPushInt(node, 0); sb.emitHelper(node, options, sb.helpers.getArgument); sb.emitHelper(node, options, sb.helpers.case(invocationVerifyCases.map((propCase) => ({ ...propCase, whenTrue: () => { handleInvokeVerify(propCase.whenTrue); }, })), handleDefaultInvokeVerify)); }, }, { condition: () => { sb.emitOp(node, 'DUP'); sb.emitPushInt(node, TransactionType.Claim); sb.emitOp(node, 'NUMEQUAL'); }, whenTrue: () => { sb.emitOp(node, 'DROP'); sb.emitHelper(node, options, sb.helpers.didReceiveNonClaimAssets); sb.emitHelper(node, options, sb.helpers.didSendAssets); sb.emitHelper(node, options, sb.helpers.if({ condition: () => { sb.emitOp(node, 'BOOLOR'); }, whenTrue: () => { sb.emitPushBoolean(node, false); }, whenFalse: () => { sb.emitPushInt(node, 0); sb.emitHelper(node, options, sb.helpers.getArgument); sb.emitHelper(node, options, sb.helpers.case(claimCases.map((propCase) => ({ ...propCase, whenTrue: () => { handleClaimVerify(propCase.whenTrue); }, })), throwDefault)); }, })); }, }, ], throwDefault)); }, }, ], throwDefault)); } } //# sourceMappingURL=InvokeSmartContractHelper.js.map