@neo-one/smart-contract-compiler
Version:
NEO•ONE TypeScript smart contract compiler.
248 lines (246 loc) • 11.8 kB
JavaScript
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