// Copyright (c) 2018 Jordi Baylina // License: LGPL-3.0+ // import ethers from "ethers"; import {Scalar} from "ffjavascript"; export default class Contract { constructor() { this.code = []; this.labels = {}; this.pendingLabels = {}; } createTxData() { let C; // Check all labels are defined const pendingLabels = Object.keys(this.pendingLabels); if (pendingLabels.length>0) { throw new Error("Lables not defined: "+ pendingLabels.join(", ")); } let setLoaderLength = 0; let genLoadedLength = -1; while (genLoadedLength!=setLoaderLength) { setLoaderLength = genLoadedLength; C = new Contract(); C.codesize(); C.push(setLoaderLength); C.push(0); C.codecopy(); C.push(this.code.length); C.push(0); C.return(); genLoadedLength = C.code.length; } return ethers.utils.hexlify(C.code.concat(this.code)); } stop() { this.code.push(0x00); } add() { this.code.push(0x01); } mul() { this.code.push(0x02); } sub() { this.code.push(0x03); } div() { this.code.push(0x04); } sdiv() { this.code.push(0x05); } mod() { this.code.push(0x06); } smod() { this.code.push(0x07); } addmod() { this.code.push(0x08); } mulmod() { this.code.push(0x09); } exp() { this.code.push(0x0a); } signextend() { this.code.push(0x0b); } lt() { this.code.push(0x10); } gt() { this.code.push(0x11); } slt() { this.code.push(0x12); } sgt() { this.code.push(0x13); } eq() { this.code.push(0x14); } iszero() { this.code.push(0x15); } and() { this.code.push(0x16); } or() { this.code.push(0x17); } shor() { this.code.push(0x18); } not() { this.code.push(0x19); } byte() { this.code.push(0x1a); } keccak() { this.code.push(0x20); } sha3() { this.code.push(0x20); } // alias address() { this.code.push(0x30); } balance() { this.code.push(0x31); } origin() { this.code.push(0x32); } caller() { this.code.push(0x33); } callvalue() { this.code.push(0x34); } calldataload() { this.code.push(0x35); } calldatasize() { this.code.push(0x36); } calldatacopy() { this.code.push(0x37); } codesize() { this.code.push(0x38); } codecopy() { this.code.push(0x39); } gasprice() { this.code.push(0x3a); } extcodesize() { this.code.push(0x3b); } extcodecopy() { this.code.push(0x3c); } returndatasize() { this.code.push(0x3d); } returndatacopy() { this.code.push(0x3e); } blockhash() { this.code.push(0x40); } coinbase() { this.code.push(0x41); } timestamp() { this.code.push(0x42); } number() { this.code.push(0x43); } difficulty() { this.code.push(0x44); } gaslimit() { this.code.push(0x45); } pop() { this.code.push(0x50); } mload() { this.code.push(0x51); } mstore() { this.code.push(0x52); } mstore8() { this.code.push(0x53); } sload() { this.code.push(0x54); } sstore() { this.code.push(0x55); } _pushLabel(label) { if (typeof this.labels[label] != "undefined") { this.push(this.labels[label]); } else { this.pendingLabels[label] = this.pendingLabels[label] || []; this.pendingLabels[label].push(this.code.length); this.push("0x000000"); } } _fillLabel(label) { if (!this.pendingLabels[label]) return; let dst = this.labels[label]; const dst3 = [dst >> 16, (dst >> 8) & 0xFF, dst & 0xFF]; this.pendingLabels[label].forEach((p) => { for (let i=0; i<3; i++) { this.code[p+i+1] = dst3[i]; } }); delete this.pendingLabels[label]; } jmp(label) { if (typeof label !== "undefined") { this._pushLabel(label); } this.code.push(0x56); } jmpi(label) { if (typeof label !== "undefined") { this._pushLabel(label); } this.code.push(0x57); } pc() { this.code.push(0x58); } msize() { this.code.push(0x59); } gas() { this.code.push(0x5a); } label(name) { if (typeof this.labels[name] != "undefined") { throw new Error("Label already defined"); } this.labels[name] = this.code.length; this.code.push(0x5b); this._fillLabel(name); } push(data) { if ((typeof data !== "string") || (data.slice(0,2) != "0x")) { let v = Scalar.e(data); if (Scalar.isNegative(v)) { v = Scalar.add(Scalar.shl(Scalar.e(1), 256), v); } let S = Scalar.toString(v, 16); if (S.length % 2) S = "0"+S; S = "0x" +S; data = S; } const d = ethers.utils.arrayify(data); if (d.length == 0 || d.length > 32) { throw new Error("Assertion failed"); } const a = []; this.code.push(0x5F + d.length); for (let i=0; i= 16) { throw new Error("Assertion failed"); } this.code.push(0x80 + n); } swap(n) { if (n < 1 || n > 16) { throw new Error("Assertion failed"); } this.code.push(0x8f + n); } log0() { this.code.push(0xa0); } log1() { this.code.push(0xa1); } log2() { this.code.push(0xa2); } log3() { this.code.push(0xa3); } log4() { this.code.push(0xa4); } create() { this.code.push(0xf0); } call() { this.code.push(0xf1); } callcode() { this.code.push(0xf2); } return() { this.code.push(0xf3); } delegatecall() { this.code.push(0xf4); } staticcall() { this.code.push(0xfa); } revert() { this.code.push(0xfd); } invalid() { this.code.push(0xfe); } selfdestruct() { this.code.push(0xff); } }