├── e2e ├── fi-samples │ ├── blank_test.fi │ ├── input_test.fi │ ├── mutez_test.fi │ ├── bool_test.fi │ ├── optional_test.fi │ ├── list_test.fi │ ├── timestamp_test.fi │ ├── string_test.fi │ ├── storage_test.fi │ ├── signature_test.fi │ ├── set_test.fi │ ├── bigmap_test.fi │ ├── map_test.fi │ ├── key_hash_test.fi │ ├── bytes_test.fi │ ├── numbers_test.fi │ ├── key_test.fi │ └── all_test.fi ├── test.js └── __snapshots__ │ └── test.js.snap ├── .gitignore ├── webpack.config.js ├── jest.config.js ├── .eslintrc.js ├── .editorconfig ├── lib ├── helper.js ├── format.js ├── parse.js ├── compile │ ├── methods │ │ ├── none.js │ │ ├── to_optional.js │ │ ├── throw.js │ │ ├── assert.js │ │ ├── pack.js │ │ ├── abs.js │ │ ├── to_pkh.js │ │ ├── unpack.js │ │ ├── neg.js │ │ ├── isset.js │ │ ├── set.js │ │ ├── to_some.js │ │ ├── sqr.js │ │ ├── get.js │ │ ├── pop.js │ │ ├── delegate.js │ │ ├── length.js │ │ ├── to_int.js │ │ ├── in.js │ │ ├── to_mutez.js │ │ ├── to_address.js │ │ ├── hash.js │ │ ├── concat.js │ │ ├── concat_ws.js │ │ ├── to_nat.js │ │ ├── verify.js │ │ ├── drop.js │ │ ├── slice.js │ │ ├── if.js │ │ ├── to_contract.js │ │ ├── mul.js │ │ ├── mod.js │ │ ├── div.js │ │ ├── add.js │ │ ├── sub.js │ │ ├── transfer.js │ │ ├── new.js │ │ └── push.js │ ├── namedType.js │ ├── condition.js │ ├── type.js │ ├── code.js │ └── script.js ├── parse │ ├── typeList.js │ ├── type.js │ ├── condition.js │ └── main.js ├── core.js ├── abi.js ├── compile.js └── abi │ └── helper.js ├── package.json ├── index.js └── README.md /e2e/fi-samples/blank_test.fi: -------------------------------------------------------------------------------- 1 | entry TestBlank(){ 2 | 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | tests/ 4 | index.html 5 | test.html 6 | test/*.fi.* -------------------------------------------------------------------------------- /e2e/fi-samples/input_test.fi: -------------------------------------------------------------------------------- 1 | storage nat test; 2 | 3 | entry TestInput(nat test1){ 4 | storage.test = input.test1; 5 | } -------------------------------------------------------------------------------- /e2e/fi-samples/mutez_test.fi: -------------------------------------------------------------------------------- 1 | entry TestMutez(){ 2 | let mutez test1 = AMOUNT; 3 | let mutez test2 = add(test1, AMOUNT, BALANCE); 4 | } -------------------------------------------------------------------------------- /e2e/fi-samples/bool_test.fi: -------------------------------------------------------------------------------- 1 | entry TestBool(){ 2 | let bool test1 = bool True; 3 | if (test1 == bool False){ 4 | assert(bool True); 5 | assert(test1); 6 | } 7 | } -------------------------------------------------------------------------------- /e2e/fi-samples/optional_test.fi: -------------------------------------------------------------------------------- 1 | entry TestOptional(){ 2 | let ?nat test1 = to_optional(nat 1); 3 | let bool test2 = isset(test1); 4 | let nat = to_some(test1); 5 | } -------------------------------------------------------------------------------- /e2e/fi-samples/list_test.fi: -------------------------------------------------------------------------------- 1 | entry TestList(){ 2 | let address[] test1 = new list(address); 3 | test1.push(SENDER); 4 | let address test2 = test1.pop(); 5 | let nat test3 = test1.length(); 6 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './index.js' 3 | , output: { 4 | filename: './fi-compile.min.js' 5 | , library: 'fi' 6 | } 7 | , node: { 8 | fs: 'empty' 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | coverageDirectory: 'e2e/coverage' 5 | , collectCoverageFrom: [ 6 | 'index.js' 7 | , 'lib/**/*.js' 8 | ] 9 | , verbose: true 10 | } 11 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | env: { 5 | browser: false 6 | , 'jest/globals': true 7 | , node: true 8 | } 9 | , extends: [ 10 | 'ayotte' 11 | ] 12 | , plugins: ['jest'] 13 | } 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [*.js] 10 | indent_size = 4 11 | indent_style = tab 12 | tab_width = 4 13 | -------------------------------------------------------------------------------- /e2e/fi-samples/timestamp_test.fi: -------------------------------------------------------------------------------- 1 | entry TestTimestamp(){ 2 | let timestamp test1 = NOW; 3 | let timestamp test2 = add(test1, int 100); 4 | let int test3 = sub(test2, test1); 5 | let timestamp test4 = add(int 200, NOW, sub(NOW, test1)); 6 | } -------------------------------------------------------------------------------- /lib/helper.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | findInObjArray(array, attr, value) { 3 | for (let i = 0; i < array.length; i += 1) { 4 | if (array[i][attr] === value) { 5 | return i 6 | } 7 | } 8 | return -1 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /e2e/fi-samples/string_test.fi: -------------------------------------------------------------------------------- 1 | entry TestString(){ 2 | let string test1 = string "Hello World"; 3 | let string test2 = concat(string "Hello World", test1); 4 | let nat test3 = test1.length(); 5 | let string test4 = slice(test2, nat 0, nat 10); 6 | } -------------------------------------------------------------------------------- /e2e/fi-samples/storage_test.fi: -------------------------------------------------------------------------------- 1 | storage nat test1; 2 | storage string test2; 3 | storage bytes test3; 4 | 5 | entry TestStorage(){ 6 | storage.test1 = nat 1; 7 | storage.test2 = concat(string "test", string "test"); 8 | storage.test3 = pack(storage.test2); 9 | } -------------------------------------------------------------------------------- /lib/format.js: -------------------------------------------------------------------------------- 1 | module.exports = function(core) { 2 | return { 3 | main: require('./parse/main')(core) 4 | , condition: require('./parse/condition')(core) 5 | , type: require('./parse/type')(core) 6 | , typeList: require('./parse/typeList')(core) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/parse.js: -------------------------------------------------------------------------------- 1 | module.exports = function(core) { 2 | return { 3 | main: require('./parse/main')(core) 4 | , condition: require('./parse/condition')(core) 5 | , type: require('./parse/type')(core) 6 | , typeList: require('./parse/typeList')(core) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /e2e/fi-samples/signature_test.fi: -------------------------------------------------------------------------------- 1 | entry TestSignature(){ 2 | let bool test1 = verify(bytes 0x0500, signature "edsigtgjPezMVW9zCu8ny4uMaAsT3gqbRANSNAzVBvZoFXx3ZiPifbLUUthdj4tewcCvJf3HpPfMTnML73hCi6UnFvZpvWsqTBk", key "edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym"); 3 | } -------------------------------------------------------------------------------- /e2e/fi-samples/set_test.fi: -------------------------------------------------------------------------------- 1 | entry TestSet(){ 2 | let set[nat] test1 = new set(nat); 3 | test1.push(nat 100); 4 | test1.push(nat 200); 5 | test1.push(nat 300); 6 | let nat test2 = nat 100; 7 | if (test1.in(test2)){ 8 | test1.drop(test2); 9 | } 10 | let nat test3 = test1.length(); 11 | } -------------------------------------------------------------------------------- /lib/compile/methods/none.js: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | module.exports = function(core) { 4 | return function(op) { 5 | const ret = { 6 | code: [] 7 | , type: ['option'] 8 | } 9 | const type = op.shift() 10 | ret.code = [`NONE ${core.compile.type(type)}`] 11 | ret.type.push(type) 12 | return ret 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/compile/namedType.js: -------------------------------------------------------------------------------- 1 | module.exports = function(core) { 2 | return function(p) { 3 | var p = p.slice(0) 4 | if (p.length == 0) { 5 | return 'unit' 6 | } 7 | const types = [] 8 | while (p.length) { 9 | const tt = p.shift() 10 | types.push(tt.type) 11 | } 12 | return core.compile.type(types) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /e2e/fi-samples/bigmap_test.fi: -------------------------------------------------------------------------------- 1 | storage bmap[nat=>address] bmaptest; 2 | entry TestBigmap(){ 3 | storage.bmaptest.push(nat 1, SENDER); 4 | storage.bmaptest.push(nat 2, address "tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM"); 5 | let address test2 = storage.bmaptest.get(nat 1); 6 | if (storage.bmaptest.in(nat 1)){ 7 | storage.bmaptest.drop(nat 2); 8 | } 9 | } -------------------------------------------------------------------------------- /e2e/fi-samples/map_test.fi: -------------------------------------------------------------------------------- 1 | entry TestMap(){ 2 | let map[nat=>address] test1 = new map(nat, address); 3 | test1.push(nat 1, SENDER); 4 | test1.push(nat 2, address "tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM"); 5 | let address test2 = test1.get(nat 1); 6 | if (test1.in(nat 1)){ 7 | test1.drop(nat 2); 8 | } 9 | let nat test3 = test1.length(); 10 | } -------------------------------------------------------------------------------- /e2e/fi-samples/key_hash_test.fi: -------------------------------------------------------------------------------- 1 | entry TestPkh(){ 2 | let pkh test1 = pkh "tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM"; 3 | let address test2 = to_address(pkh "tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM"); 4 | let contract[unit] test3 = to_contract(test1); 5 | 6 | transfer(test1, mutez 0); 7 | transfer(test2, mutez 0); 8 | transfer(test3, mutez 0); 9 | 10 | delegate(); 11 | delegate(test1); 12 | delegate(pkh "tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM"); 13 | } -------------------------------------------------------------------------------- /lib/parse/typeList.js: -------------------------------------------------------------------------------- 1 | // Converts a list of named unparsed-types into an object of named parsed-types 2 | // [['nat', 'age'],['string', 'name']] 3 | module.exports = function(core) { 4 | return function(p) { 5 | const ret = [] 6 | for (let i = 0; i < p.length; i++) { 7 | if (p[i].length <= 0) { 8 | continue 9 | } 10 | ret.push({ 11 | name: p[i][1] 12 | , type: core.parse.type(p[i][0]) 13 | }) 14 | } 15 | return ret 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/compile/methods/to_optional.js: -------------------------------------------------------------------------------- 1 | /* 2 | to_optional(_) => option _ 3 | */ 4 | module.exports = function(core) { 5 | return function(op) { 6 | const ret = { 7 | code: [] 8 | , type: ['option'] 9 | } 10 | if (op.length != 1) { 11 | throw 'Invalid arguments for function, expects 1' 12 | } 13 | const a1 = core.compile.code(op.shift()) 14 | ret.code = a1.code 15 | ret.code.push(core.compile.ml('some')) 16 | ret.type.push(a1.type) 17 | return ret 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/compile/methods/throw.js: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | module.exports = function(core) { 4 | return function(op) { 5 | const ret = { 6 | code: [] 7 | , type: false 8 | } 9 | if (op.length) { 10 | if (typeof op[0] == 'string') { 11 | ret.code = core.compile.code(op).code 12 | } 13 | else { 14 | ret.code = core.compile.code(op.shift()).code 15 | } 16 | } 17 | else { 18 | ret.code = ['UNIT'] 19 | } 20 | ret.code.push('FAILWITH') 21 | return ret 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/core.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | map: {} 5 | , abi: {} 6 | , literalTypes: [ 7 | 'address' 8 | , 'bool' 9 | , 'bytes' 10 | , 'int' 11 | , 'key' 12 | , 'key_hash' 13 | , 'mutez' 14 | , 'nat' 15 | , 'operation' 16 | , 'pkh' 17 | , 'signature' 18 | , 'string' 19 | , 'timestamp' 20 | , 'unit' 21 | ] 22 | , complexTypes: [ 23 | 'big_map' 24 | , 'contract' 25 | , 'list' 26 | , 'map' 27 | , 'option' 28 | , 'set' 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /e2e/fi-samples/bytes_test.fi: -------------------------------------------------------------------------------- 1 | entry TestBytes(){ 2 | let bytes test1 = bytes 0x0500; 3 | test1 = concat(test1, bytes 0x050001); 4 | let bytes test2 = hash(bytes 0x050001); 5 | let bytes test3 = hash(bytes 0x050001, blake2b); 6 | let bytes test4 = hash(bytes 0x050001, sha256); 7 | let bytes test5 = hash(bytes 0x050001, sha512); 8 | let bytes test6 = pack(nat 1); 9 | let nat test7 = unpack(test3, nat); 10 | let nat test8 = test6.length(); 11 | let bytes test9 = slice(test2, nat 0, nat 4); 12 | } -------------------------------------------------------------------------------- /lib/compile/methods/assert.js: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | module.exports = function(core) { 4 | return function(op) { 5 | const ret = { 6 | code: [] 7 | , type: false 8 | } 9 | const condition = core.compile.condition(op.shift()) 10 | ret.code = condition 11 | 12 | let er 13 | if (op.length) { 14 | er = core.compile.code(op.shift()).code 15 | } 16 | else { 17 | er = ['PUSH string "Failed assert"'] 18 | } 19 | er.push('FAILWITH') 20 | 21 | ret.code.push(['IF', [], er]) 22 | return ret 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/compile/methods/pack.js: -------------------------------------------------------------------------------- 1 | /* 2 | pack(_) => bytes 3 | */ 4 | module.exports = function(core) { 5 | return function(op) { 6 | const ret = { 7 | code: [] 8 | , type: ['bytes'] 9 | } 10 | if (op.length != 1) { 11 | throw 'Not enough arguments for function pack, expects 1' 12 | } 13 | const a1 = core.compile.code(op.shift()) 14 | if (!a1.type) { 15 | throw 'Stack error for function pack' 16 | } 17 | ret.code = a1.code 18 | ret.code.push(core.compile.ml('pack')) 19 | return ret 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/compile/methods/abs.js: -------------------------------------------------------------------------------- 1 | /* 2 | abs(int) => nat 3 | */ 4 | module.exports = function(core) { 5 | return function(op) { 6 | const ret = { 7 | code: [] 8 | , type: ['nat'] 9 | } 10 | if (op.length != 1) { 11 | throw 'Not enough arguments for function abs, expects 1' 12 | } 13 | const a1 = core.compile.code(op.shift()) 14 | if (a1.type[0] != 'int') { 15 | throw `Invalid type for abs, expects int not ${a1.type[0]}` 16 | } 17 | ret.code = a1.code 18 | ret.code.push('ABS') 19 | return ret 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/compile/methods/to_pkh.js: -------------------------------------------------------------------------------- 1 | /* 2 | to_pkh(key) => key_hash 3 | */ 4 | module.exports = function(core) { 5 | return function(op) { 6 | const ret = { 7 | code: [] 8 | , type: ['key_hash'] 9 | } 10 | if (op.length != 1) { 11 | throw 'Invalid arguments for function, expects 1' 12 | } 13 | const a1 = core.compile.code(op.shift()) 14 | if (a1.type[0] != 'key') { 15 | throw `Invalid type for to_pkh, expects key not ${a1.type[0]}` 16 | } 17 | ret.code = a1.code 18 | ret.code.push('HASH_KEY') 19 | return ret 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/compile/methods/unpack.js: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | module.exports = function(core) { 4 | return function(op) { 5 | const ret = { 6 | code: [] 7 | , type: false 8 | } 9 | if (op.length != 2) { 10 | throw 'Invalid arguments for function unpack, expects 2' 11 | } 12 | const bytes = core.compile.code(op.shift()); const type = op.shift() 13 | ret.code = bytes.code 14 | ret.code.push(`UNPACK ${core.compile.type(type)}`) 15 | ret.code = ret.code.concat(core.compile.error('Unable to unpack')) 16 | ret.type = type 17 | return ret 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /e2e/fi-samples/numbers_test.fi: -------------------------------------------------------------------------------- 1 | entry TestNumbers(){ 2 | let nat test1 = nat 100; 3 | let nat test2 = add(test1, nat 2, nat 3); 4 | let nat test3 = to_nat(sub(add(to_int(mutez 0), int 2), int 3, nat 5)); 5 | let int test4 = to_int(mutez 0); 6 | let mutez test5 = to_mutez(sub(add(to_int(mutez 0), int 2), int 3, nat 5)); 7 | let nat test6 = abs(to_int(test2)); 8 | let nat test7 = sqr(test6); 9 | let nat test8 = abs(to_int(test2)); 10 | let nat test9 = abs(to_int(test2)); 11 | let nat test10 = mod(nat 10, nat 3); 12 | let nat test11 = div(nat 10, nat 3); 13 | } -------------------------------------------------------------------------------- /lib/compile/methods/neg.js: -------------------------------------------------------------------------------- 1 | /* 2 | neg(nat) => int 3 | neg(int) => int 4 | */ 5 | module.exports = function(core) { 6 | return function(op) { 7 | const ret = { 8 | code: [] 9 | , type: ['int'] 10 | } 11 | if (op.length != 1) { 12 | throw 'Not enough arguments for function neg, expects 1' 13 | } 14 | const a1 = core.compile.code(op.shift()) 15 | if (['nat', 'int'].indexOf(a1.type[0]) < 0) { 16 | throw `Invalid type for neg, expects nat or int not ${a1.type[0]}` 17 | } 18 | ret.code = a1.code 19 | ret.code.push(core.compile.ml('neg')) 20 | return ret 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/compile/methods/isset.js: -------------------------------------------------------------------------------- 1 | /* 2 | isset(option) => bool 3 | */ 4 | module.exports = function(core) { 5 | return function(op) { 6 | const ret = { 7 | code: [] 8 | , type: ['bool'] 9 | } 10 | if (op.length != 1) { 11 | throw 'Invalid arguments for function hash, expects 1' 12 | } 13 | const v = core.compile.code(op.shift()) 14 | if (v.type[0] != 'option') { 15 | throw `Invalid type for isset, expects an optional value not ${v.type[0]}` 16 | } 17 | ret.code = v.code 18 | ret.code.push(['IF_NONE', ['PUSH bool False'], ['DROP', 'PUSH bool True']]) 19 | return ret 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/compile/methods/set.js: -------------------------------------------------------------------------------- 1 | // auto-instruction 2 | module.exports = function(core) { 3 | return function(op) { 4 | const ret = { 5 | code: [] 6 | , type: false 7 | } 8 | if (op.length < 2) { 9 | throw 'Not enough arguments for function' 10 | } 11 | const vName = op.shift(); const v = core.compile.setter(vName); const val = core.compile.code(op.shift()) 12 | if (core.compile.type(v.type) != core.compile.type(val.type)) { 13 | throw `Type mismatch for ${vName} - expecting ${v.type[0]} not ${val.type[0]}` 14 | } 15 | ret.code = val.code 16 | ret.code = ret.code.concat(v.code) 17 | return ret 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/compile/methods/to_some.js: -------------------------------------------------------------------------------- 1 | /* 2 | to_some(option _) => _ 3 | */ 4 | module.exports = function(core) { 5 | return function(op) { 6 | const ret = { 7 | code: [] 8 | , type: '' 9 | } 10 | if (op.length != 1) { 11 | throw 'Invalid arguments for function, expects 1' 12 | } 13 | const a1 = core.compile.code(op.shift()) 14 | if (a1.type[0] != 'option') { 15 | throw `Invalid type for to_some, expects optional value not ${a1.type[0]}` 16 | } 17 | ret.code = a1.code 18 | ret.code.push(['IF_NONE', ['PUSH string "Optional value is empty"', 'FAILWITH'], []]) 19 | ret.type = a1.type[1] 20 | return ret 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/compile/methods/sqr.js: -------------------------------------------------------------------------------- 1 | /* 2 | sqr(mutez) => mutez 3 | sqr(nat) => nat 4 | sqr(int) => int 5 | */ 6 | module.exports = function(core) { 7 | return function(op) { 8 | const ret = { 9 | code: [] 10 | , type: false 11 | } 12 | if (op.length != 1) { 13 | throw 'Not enough arguments for sqr, expects 1' 14 | } 15 | const a1 = core.compile.code(op.shift()) 16 | if (['nat', 'int', 'mutez'].indexOf(a1.type[0]) < 0) { 17 | throw `Invalid type for sqr, expects nat, int or mutez not ${a1.type[0]}` 18 | } 19 | ret.code = a1.code 20 | ret.code.push('DUP') 21 | ret.code.push('MUL') 22 | ret.type = a1.type 23 | return ret 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/compile/methods/get.js: -------------------------------------------------------------------------------- 1 | /* 2 | get(xx, _ key) => _ 3 | xx.get(_ key) => _ 4 | *Note xx is of type map, big_map 5 | */ 6 | module.exports = function(core) { 7 | return function(op) { 8 | const ret = { 9 | code: [] 10 | , type: false 11 | } 12 | const map = core.compile.code(op.shift()); const key = core.compile.code(op.shift()) 13 | if (['map', 'big_map'].indexOf(map.type[0]) < 0) { 14 | throw `Invalid type for get, expect map or big_map not ${map.type[0]}` 15 | } 16 | ret.code = key.code 17 | ret.code.push(['DIP', map.code]) 18 | ret.code.push('GET') 19 | ret.code.push(['IF_NONE', ['PUSH string "Key not found in map"', 'FAILWITH'], []]) 20 | ret.type = map.type[2] 21 | return ret 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /e2e/fi-samples/key_test.fi: -------------------------------------------------------------------------------- 1 | entry TestKey(){ 2 | let key test1 = key "edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym"; 3 | let pkh test2 = to_pkh(key "edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym"); 4 | let address test3 = to_address(key "edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym"); 5 | let contract[unit] test4 = to_contract(test1); 6 | let address test5 = to_address(test4); 7 | 8 | transfer(test1, mutez 0); 9 | transfer(test2, mutez 0); 10 | transfer(test3, mutez 0); 11 | transfer(test4, mutez 0); 12 | transfer(test5, mutez 0); 13 | 14 | let bool test6 = verify(bytes 0x0500, signature "edsigtgjPezMVW9zCu8ny4uMaAsT3gqbRANSNAzVBvZoFXx3ZiPifbLUUthdj4tewcCvJf3HpPfMTnML73hCi6UnFvZpvWsqTBk", test1); 15 | } -------------------------------------------------------------------------------- /lib/compile/methods/pop.js: -------------------------------------------------------------------------------- 1 | /* 2 | pop(xx) 3 | xx.pop() 4 | *Note xx is of type list 5 | */ 6 | module.exports = function(core) { 7 | return function(op) { 8 | const ret = { 9 | code: [] 10 | , type: false 11 | } 12 | if (op.length != 1) { 13 | throw 'Not enough arguments for function pack, expects 1' 14 | } 15 | const listName = op.shift(); const list = core.compile.code(listName) 16 | if (['list'].indexOf(list.type[0]) < 0) { 17 | throw `Invalid type for pop, expects list not ${list.type[0]}` 18 | } 19 | ret.type = list.type[1] 20 | 21 | ret.code = list.code 22 | ret.code.push(['IF_CONS', [], ['PUSH string "Fail pop"', 'FAILWITH']]) 23 | ret.code.push(['DIP', core.compile.setter(listName[0]).code]) 24 | return ret 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/compile/methods/delegate.js: -------------------------------------------------------------------------------- 1 | /* 2 | delegate() 3 | delegate(key_hash) 4 | */ 5 | module.exports = function(core) { 6 | return function(op) { 7 | const ret = { 8 | code: [] 9 | , type: false 10 | } 11 | if (op.length > 1) { 12 | throw 'Invalid argument count for delegate - expecting 0 or 1' 13 | } 14 | if (op.length) { 15 | const delegate = core.compile.code(op.shift()) 16 | if (delegate.type[0] != 'key_hash') { 17 | throw `Invalid type for delegate, expecting key_hash not ${delegate.type[0]}` 18 | } 19 | ret.code = delegate.code 20 | ret.code.push('SOME') 21 | } 22 | else { 23 | ret.code = ['NONE key_hash'] 24 | } 25 | ret.code.push('SET_DELEGATE') 26 | ret.code = ret.code.concat(core.compile.operation()) 27 | return ret 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/compile/methods/length.js: -------------------------------------------------------------------------------- 1 | /* 2 | length(map xx) => nat 3 | length(list xx) => nat 4 | length(set xx) => nat 5 | length(string xx) => nat 6 | length(bytes xx) => nat 7 | xx.length() => nat 8 | *Note xx is of type list, map or set, string or bytes 9 | */ 10 | module.exports = function(core) { 11 | return function(op) { 12 | const ret = { 13 | code: [] 14 | , type: ['nat'] 15 | } 16 | if (op.length != 1) { 17 | throw 'Invalid arguments for function length, expects 1' 18 | } 19 | const val = core.compile.code(op.shift()) 20 | if (['map', 'list', 'set', 'string', 'bytes'].indexOf(val.type[0]) < 0) { 21 | throw `Invalid type for length, expects map, list, set, string or bytes not ${val.type[0]}` 22 | } 23 | ret.code = val.code 24 | ret.code.push('SIZE') 25 | return ret 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/abi.js: -------------------------------------------------------------------------------- 1 | let abi = {} 2 | module.exports = function(core) { 3 | helper = require('./abi/helper')(core) 4 | return { 5 | load(a) { 6 | if (typeof a == 'string') { 7 | a = JSON.parse(a) 8 | } 9 | abi = a 10 | } 11 | , entry(entry, data) { 12 | const i = core.helper.findInObjArray(abi.entry, 'name', entry) 13 | if (i < 0) { 14 | throw 'Error with call' 15 | } 16 | var entry = abi.entry[core.helper.findInObjArray(abi.entry, 'name', entry)] 17 | if (typeof data != 'string') { 18 | data = helper.namedTypevalue(entry.input, data, abi).toString() 19 | } 20 | return entry.id + helper.packData(data) 21 | } 22 | , storage(data) { 23 | if (typeof data != 'string') { 24 | data = helper.namedTypevalue(abi.storage, data, abi).toString() 25 | } 26 | return data 27 | } 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /lib/compile/methods/to_int.js: -------------------------------------------------------------------------------- 1 | /* 2 | to_int(mutez) => int 3 | to_int(nat) => int 4 | */ 5 | module.exports = function(core) { 6 | return function(op) { 7 | const ret = { 8 | code: [] 9 | , type: ['int'] 10 | } 11 | if (op.length != 1) { 12 | throw 'Invalid arguments for function to_int, expects 1' 13 | } 14 | const a1 = core.compile.code(op.shift()) 15 | if (['mutez', 'nat'].indexOf(a1.type[0]) < 0) { 16 | throw `Argument for to_int must be mutez, nat or int not ${a1.type[0]}` 17 | } 18 | ret.code = a1.code 19 | if (a1.type[0] == 'mutez') { 20 | ret.code.push(['DIP', ['PUSH mutez 1']]) 21 | ret.code.push('EDIV') 22 | ret.code = ret.code.concat(core.compile.error('Error with casting')) 23 | ret.code.push('CAR') 24 | } 25 | ret.code.push('PUSH int 1') 26 | ret.code.push('MUL') 27 | return ret 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/compile/methods/in.js: -------------------------------------------------------------------------------- 1 | /* 2 | in(xx, _ key) => bool 3 | xx.in(_ key) => bool 4 | *Note xx is of type map, big_map or set 5 | */ 6 | module.exports = function(core) { 7 | return function(op) { 8 | const ret = { 9 | code: [] 10 | , type: ['bool'] 11 | } 12 | if (op.length != 2) { 13 | throw 'Invalid arguments for function in, expects 2' 14 | } 15 | const vv = core.compile.code(op.shift()); const key = core.compile.code(op.shift()) 16 | if (['map', 'big_map', 'set'].indexOf(vv.type[0]) < 0) { 17 | throw `Invalid type for in, expects map, big_map or set not ${vv.type[0]}` 18 | } 19 | if (core.compile.type(key.type[0]) != core.compile.type(vv.type[1])) { 20 | throw `Invalid key type, expects ${vv.type[1]} not ${key.type[0]}` 21 | } 22 | ret.code = key.code 23 | ret.code.push(['DIP', vv.code]) 24 | ret.code.push('MEM') 25 | return ret 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/compile/methods/to_mutez.js: -------------------------------------------------------------------------------- 1 | /* 2 | to_mutez(int) => mutez 3 | to_mutez(nat) => mutez 4 | */ 5 | module.exports = function(core) { 6 | return function(op) { 7 | const ret = { 8 | code: [] 9 | , type: ['mutez'] 10 | } 11 | if (op.length != 1) { 12 | throw 'Invalid arguments for function to_mutez, expects 1' 13 | } 14 | const a1 = core.compile.code(op.shift()) 15 | if (['nat', 'int'].indexOf(a1.type[0]) < 0) { 16 | throw `Argument for to_mutez must be mutez, nat or int not ${a1.type[0]}` 17 | } 18 | ret.code = a1.code 19 | if (a1.type[0] == 'int') { 20 | ret.code.push('DUP') 21 | ret.code.push('GT') 22 | ret.code.push(['IF', [], ['PUSH string "Mutez conversion not possible"', 'FAILWITH']]) 23 | ret.code.push('ABS') 24 | } 25 | ret.code.push(['DIP', ['PUSH mutez 1']]) 26 | ret.code.push(core.compile.ml('mul')) 27 | return ret 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fi-compiler", 3 | "version": "0.2.0", 4 | "description": "Core compiler library for fi, a high-level language for Michelson", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "lint": "eslint --ext .js --ignore-pattern dist/* .", 9 | "test": "jest", 10 | "coverage": "jest --coverage" 11 | }, 12 | "author": "TezTech Labs", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "eslint": "^5.15.1", 16 | "eslint-config-ayotte": "^1.3.12", 17 | "eslint-plugin-import": "^2.16.0", 18 | "eslint-plugin-jest": "^22.3.2", 19 | "eslint-plugin-promise": "^4.0.1", 20 | "jest": "^24.5.0", 21 | "uglifyjs-webpack-plugin": "^2.1.2", 22 | "webpack": "^4.29.6", 23 | "webpack-cli": "^3.2.3" 24 | }, 25 | "dependencies": { 26 | "bignumber.js": "^8.1.1", 27 | "js-sha256": "^0.9.0", 28 | "text-encoding": "^0.7.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/compile/methods/to_address.js: -------------------------------------------------------------------------------- 1 | /* 2 | to_address(key) => address 3 | to_address(key_hash) => address 4 | to_address(contract _) => address 5 | */ 6 | module.exports = function(core) { 7 | return function(op) { 8 | const ret = { 9 | code: [] 10 | , type: ['address'] 11 | } 12 | if (op.length != 1) { 13 | throw 'Invalid arguments for function to_address, expects 1' 14 | } 15 | const a1 = core.compile.code(op.shift()) 16 | if (['contract', 'key_hash', 'key'].indexOf(a1.type[0]) < 0) { 17 | throw `Invalid argument type for to_address, expects contract, key_hash or key not ${a1.type[0]}` 18 | } 19 | ret.code = a1.code 20 | if (a1.type[0] == 'key_hash' || a1.type[0] == 'key') { 21 | if (a1.type[0] == 'key') { 22 | ret.code.push(core.compile.ml('hash_key')) 23 | } 24 | ret.code.push(core.compile.ml('implicit_account')) 25 | } 26 | ret.code.push(core.compile.ml('address')) 27 | return ret 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/compile/methods/hash.js: -------------------------------------------------------------------------------- 1 | /* 2 | hash(bytes, algo) => bytes 3 | hash(bytes) => bytes 4 | *Note: algo must be either 'blake2b', 'sha256' or 'sha512'. blake2b by default 5 | */ 6 | module.exports = function(core) { 7 | return function(op) { 8 | const ret = { 9 | code: [] 10 | , type: ['bytes'] 11 | } 12 | if (op.length < 1 || op.length > 2) { 13 | throw 'Invalid arguments for function hash, expects 1 or 2' 14 | } 15 | let algo; const data = core.compile.code(op.shift()) 16 | if (data.type[0] != 'bytes') { 17 | throw `Invalid type for hash, expect bytes not ${data.type[0]}` 18 | } 19 | if (!op.length) { 20 | algo = 'blake2b' 21 | } 22 | else { 23 | algo = op.shift()[0] 24 | } 25 | if (['blake2b', 'sha256', 'sha512'].indexOf(algo) < 0) { 26 | throw `Unknown hash algo ${algo} - expects blake2b, sha256 or sha512` 27 | } 28 | ret.code = data.code 29 | ret.code.push(core.compile.ml(algo)) 30 | return ret 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/compile/methods/concat.js: -------------------------------------------------------------------------------- 1 | /* 2 | concat(string, string, ...) => string 3 | concat(bytes, bytes, ...) => bytes 4 | */ 5 | module.exports = function(core) { 6 | return function(op) { 7 | const ret = { 8 | code: [] 9 | , type: false 10 | } 11 | if (op.length < 2) { 12 | throw 'Not enough arguments for function concat - expects at least two' 13 | } 14 | const instr = core.compile.ml('concat'); const a1 = core.compile.code(op.shift()); let an 15 | if (['string', 'bytes'].indexOf(a1.type[0]) < 0) { 16 | throw `Invalid type for concat, expecting string or bytes not ${a1.type[0]}` 17 | } 18 | ret.code = a1.code 19 | ret.type = a1.type 20 | while (op.length) { 21 | an = core.compile.code(op.shift()) 22 | if (an.type[0] != ret.type[0]) { 23 | throw `Invalid type for concat, expecting ${ret.type[0]} not ${an.type[0]}` 24 | } 25 | ret.code.push(['DIP', an.code]) 26 | ret.code.push(instr) 27 | } 28 | return ret 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/compile/methods/concat_ws.js: -------------------------------------------------------------------------------- 1 | /* 2 | concat_ws(string sperator, string....) => string 3 | */ 4 | module.exports = function(core) { 5 | return function(op) { 6 | const ret = { 7 | code: [] 8 | , type: ['string'] 9 | } 10 | const sep = core.compile.code(op.shift()) 11 | const a1 = core.compile.code(op.shift()); let an 12 | if (sep.type[0] != 'string') { 13 | throw `Invalid type for seperator, expecting string not ${sep.type[0]}` 14 | } 15 | if (a1.type[0] != 'string') { 16 | throw `Invalid type for concat_ws, expecting string now ${a1.type[0]}` 17 | } 18 | ret.code = a1.code 19 | while (op.length) { 20 | ret.code.push(['DIP', sep.code]) 21 | ret.code.push('CONCAT') 22 | an = core.compile.code(op.shift()) 23 | if (an.type[0] != 'string') { 24 | throw `Invalid type for concat, expecting string not ${an.type[0]}` 25 | } 26 | 27 | ret.code.push(['DIP', an.code]) 28 | ret.code.push('CONCAT') 29 | } 30 | return ret 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/compile/methods/to_nat.js: -------------------------------------------------------------------------------- 1 | /* 2 | to_nat(mutez) => nat 3 | to_nat(int) => nat 4 | */ 5 | module.exports = function(core) { 6 | return function(op) { 7 | const ret = { 8 | code: [] 9 | , type: ['nat'] 10 | } 11 | if (op.length != 1) { 12 | throw 'Invalid arguments for function to_nat, expects 1' 13 | } 14 | const a1 = core.compile.code(op.shift()) 15 | if (['mutez', 'int'].indexOf(a1.type[0]) < 0) { 16 | throw `Invalid type for to_nat, expects mutez, nat or int not ${a1.type[0]}` 17 | } 18 | ret.code = a1.code 19 | if (a1.type[0] == 'mutez') { 20 | ret.code.push(['DIP', ['PUSH mutez 1']]) 21 | ret.code.push('EDIV') 22 | ret.code = ret.code.concat(core.compile.error('Error with casting')) 23 | ret.code.push('CAR') 24 | } 25 | else { 26 | ret.code.push('DUP') 27 | ret.code.push('GT') 28 | ret.code.push(['IF', [], ['PUSH string "Nat conversion not possible"', 'FAILWITH']]) 29 | ret.code.push('ABS') 30 | } 31 | return ret 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/compile/methods/verify.js: -------------------------------------------------------------------------------- 1 | /* 2 | verify(bytes data, signature signature, key) => bool 3 | */ 4 | module.exports = function(core) { 5 | return function(op) { 6 | const ret = { 7 | code: [] 8 | , type: ['bool'] 9 | } 10 | if (op.length != 3) { 11 | throw 'Invalid arguments for verify function, expects 3' 12 | } 13 | const bytes = core.compile.code(op.shift()); const sig = core.compile.code(op.shift()); const key = core.compile.code(op.shift()) 14 | 15 | if (key.type[0] != 'key') { 16 | throw `Invalid type for key, expecting key not ${key.type[0]}` 17 | } 18 | if (bytes.type[0] != 'bytes') { 19 | throw `Invalid type for data, expecting bytes not ${bytes.type[0]}` 20 | } 21 | if (sig.type[0] != 'signature') { 22 | throw `Invalid type for signature, expecting signature not ${sig.type[0]}` 23 | } 24 | 25 | ret.code = key.code 26 | ret.code.push(['DIP', sig.code]) 27 | ret.code.push(['DIIP', bytes.code]) 28 | ret.code.push('CHECK_SIGNATURE') 29 | return ret 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const core = require('./lib/core') 4 | core.sha256 = require('js-sha256') 5 | core.helper = require('./lib/helper') 6 | core.parse = require('./lib/parse')(core) 7 | core.compile = require('./lib/compile')(core) 8 | core.abi = require('./lib/abi')(core) 9 | 10 | module.exports = { 11 | compile(ml, config = {}) { 12 | const { 13 | abi_format = 'compact' 14 | , ml_format = 'compact' 15 | , macros = true 16 | } = config 17 | 18 | if (abi_format && abi_format !== 'compact') { 19 | throw new Error(`${abi_format} is not a valid abi_format`) 20 | } 21 | 22 | if (ml_format && ml_format !== 'compact') { 23 | throw new Error(`${ml_format} is not a valid ml_format`) 24 | } 25 | 26 | if (macros && macros !== true) { 27 | throw new Error(`${macros} is not a valid macros`) 28 | } 29 | 30 | return core.compile.script(ml, { 31 | abi_format 32 | , ml_format 33 | , macros 34 | }) 35 | } 36 | , abi: core.abi 37 | , version: '0.0.1b' 38 | , _core: core 39 | } 40 | -------------------------------------------------------------------------------- /lib/compile/condition.js: -------------------------------------------------------------------------------- 1 | module.exports = function(core) { 2 | return compileCondition = function(co) { 3 | if (co.length < 2 || co.length > 3) { 4 | throw 'Error with condition' 5 | } 6 | const com = co[0]; let lhs = co[1] 7 | if (['and', 'or', 'neq', 'le', 'ge', 'lt', 'gt', 'eq'].indexOf(lhs[0]) >= 0) { 8 | lhs = compileCondition(lhs) 9 | } 10 | else { 11 | lhs = core.compile.code(lhs).code 12 | } 13 | if (co.length == 2) { 14 | rhs = ['bool', 'True'] 15 | } 16 | else { 17 | rhs = co[2] 18 | } 19 | if (['and', 'or', 'neq', 'le', 'ge', 'lt', 'gt', 'eq'].indexOf(rhs[0]) >= 0) { 20 | rhs = compileCondition(rhs) 21 | } 22 | else { 23 | rhs = core.compile.code(rhs).code 24 | } 25 | let code = lhs.concat([['DIP', rhs]]) 26 | if (['and', 'or'].indexOf(com) >= 0) { 27 | code = code.concat([com.toUpperCase()]) 28 | } 29 | else if (['neq', 'le', 'ge', 'lt', 'gt', 'eq'].indexOf(com) >= 0) { 30 | code = code.concat(['COMPARE', com.toUpperCase()]) 31 | } 32 | return code 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/compile/methods/drop.js: -------------------------------------------------------------------------------- 1 | /* 2 | drop(xx, _ key) 3 | xx.drop(_ key) 4 | *Note xx is of type map, big_map or set 5 | */ 6 | module.exports = function(core) { 7 | return function(op) { 8 | const ret = { 9 | code: [] 10 | , type: false 11 | } 12 | const vvName = op.shift(); const key = core.compile.code(op.shift()); const vv = core.compile.code(vvName) 13 | if (['map', 'big_map', 'set'].indexOf(vv.type[0]) < 0) { 14 | throw `Invalid type for drop, expecting map, big_map or set, not ${vv.type[0]}` 15 | } 16 | if (core.compile.type(key.type[0]) != core.compile.type(vv.type[1])) { 17 | throw `Invalid key type, expecting ${vv.type[1]} not ${key.type[0]}` 18 | } 19 | ret.code = key.code 20 | if (vv.type[0] == 'set') { 21 | ret.code.push(['DIP', ['PUSH bool False']]) 22 | } 23 | else { 24 | ret.code.push(['DIP', [`NONE ${core.compile.type(vv.type[2])}`]]) 25 | } 26 | ret.code.push(['DIIP', vv.code]) 27 | ret.code.push('UPDATE') 28 | ret.code = ret.code.concat(core.compile.setter(vvName[0]).code) 29 | return ret 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/compile/methods/slice.js: -------------------------------------------------------------------------------- 1 | /* 2 | slice(string, nat offset, nat length) => string 3 | slice(bytes, nat offset, nat length) => bytes 4 | */ 5 | module.exports = function(core) { 6 | return function(op) { 7 | const ret = { 8 | code: [] 9 | , type: '' 10 | } 11 | if (op.length != 3) { 12 | throw 'Invalid arguments for function slice, expects 3' 13 | } 14 | const v = core.compile.code(op.shift()); const offset = core.compile.code(op.shift()); const length = core.compile.code(op.shift()) 15 | if (['bytes', 'string'].indexOf(v.type[0]) < 0) { 16 | throw `Invalid type, expecting bytes or string not ${v.type[0]}` 17 | } 18 | if (offset.type != 'nat') { 19 | throw `Invalid type for offset, expecting nat not ${offset.type}` 20 | } 21 | if (length.type != 'nat') { 22 | throw `Invalid type for length, expecting nat not ${length.type}` 23 | } 24 | 25 | ret.code = offset.code 26 | ret.code.push(['DIP', length.code]) 27 | ret.code.push(['DIIP', v.code]) 28 | ret.code.push(core.compile.ml('slice')) 29 | ret.code = ret.code.concat(core.compile.error('Unable to slice')) 30 | ret.type = v.type 31 | return ret 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/compile/methods/if.js: -------------------------------------------------------------------------------- 1 | /* 2 | */ 3 | module.exports = function(core) { 4 | return function(op) { 5 | const ret = { 6 | code: [] 7 | , type: false 8 | } 9 | let condition = core.compile.condition(op.shift()); let cc = core.compile.code(op.shift()); let n; let toclose = 0 10 | if (cc) { 11 | const br = [condition.concat([['IF', cc.code]])] 12 | toclose++ 13 | while (op.length) { 14 | n = op.shift() 15 | if (n == 'if') { 16 | condition = core.compile.condition(op.shift()) 17 | cc = core.compile.code(op.shift()) 18 | br.push(condition.concat([['IF', cc.code]])) 19 | e++ 20 | } 21 | else { 22 | br[br.length - 1][br[br.length - 1].length - 1].push(core.compile.code(n).code) 23 | break 24 | } 25 | } 26 | if (br[br.length - 1][br[br.length - 1].length - 1].length == 2) { 27 | br[br.length - 1][br[br.length - 1].length - 1].push([]) 28 | } 29 | let bp = br.pop() 30 | while (br.length) { 31 | br[br.length - 1][br[br.length - 1].length - 1].push(bp) 32 | bp = br.pop() 33 | } 34 | ret.code = bp 35 | } 36 | else { 37 | ret.code = [condition, ['IF', [], []]] 38 | } 39 | return ret 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/compile/methods/to_contract.js: -------------------------------------------------------------------------------- 1 | /* 2 | to_contract(key) => contract unit; 3 | to_contract(key_hash) => contract unit; 4 | to_contract(address) => contract unit; 5 | to_contract(address, type) => contract type; 6 | */ 7 | module.exports = function(core) { 8 | return function(op) { 9 | const ret = { 10 | code: [] 11 | , type: ['contract'] 12 | } 13 | if (op.length < 1 || op.length > 2) { 14 | throw 'Invalid arguments for function, expects 1 or 2' 15 | } 16 | const a1 = core.compile.code(op.shift()); let type = ['unit'] 17 | ret.code = a1.code 18 | if (['address', 'key_hash', 'key'].indexOf(a1.type[0]) < 0) { 19 | throw `Invalid argument type for to_contract, expects address, key_hash or key not ${a1.type[0]}` 20 | } 21 | if (a1.type[0] == 'address') { 22 | if (op.length) { 23 | type = core.parse.type(op.shift()[0]) 24 | } 25 | ret.code.push(`CONTRACT ${core.compile.type(type)}`) 26 | ret.code = ret.code.concat(core.compile.error('Invalid contract param')) 27 | } 28 | else if (a1.type[0] == 'key_hash' || a1.type[0] == 'key') { 29 | if (a1.type[0] == 'key') { 30 | ret.code.push(core.compile.ml('hash_key')) 31 | } 32 | ret.code.push(core.compile.ml('implicit_account')) 33 | } 34 | 35 | ret.type.push(type) 36 | return ret 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/compile/methods/mul.js: -------------------------------------------------------------------------------- 1 | /* 2 | mul(int, int, ...) => int 3 | mul(int, nat, ...) => int 4 | mul(nat, int, ...) => int 5 | mul(nat, nat, ...) => nat 6 | mul(mutez, nat, ...) => mutez 7 | mul(nat, mutez, ...) => mutez 8 | */ 9 | 10 | const iotypes = { 11 | int: { 12 | int: 'int' 13 | , nat: 'int' 14 | } 15 | , nat: { 16 | int: 'int' 17 | , nat: 'nat' 18 | } 19 | , mutez: { 20 | nat: 'mutez' 21 | , mutez: 'mutez' 22 | } 23 | } 24 | 25 | module.exports = function(core) { 26 | return function(op) { 27 | const ret = { 28 | code: [] 29 | , type: false 30 | } 31 | if (op.length < 2) { 32 | throw 'Not enough arguments for mul, expects at least 2' 33 | } 34 | const instr = core.compile.ml('mul'); const a1 = core.compile.code(op.shift()); let an 35 | if (typeof iotypes[a1.type[0]] == 'undefined') { 36 | throw `Invalid type for mul, expects int, nat or mutez not ${a1.type[0]}` 37 | } 38 | ret.type = a1.type 39 | ret.code = a1.code 40 | while (op.length) { 41 | an = core.compile.code(op.shift()) 42 | if (typeof iotypes[ret.type[0]][an.type[0]] == 'undefined') { 43 | throw `Invalid type for mul ${an.type[0]}` 44 | } 45 | ret.type = [iotypes[ret.type[0]][an.type[0]]] 46 | ret.code.push(['DIP', an.code]) 47 | ret.code.push(instr) 48 | } 49 | return ret 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/compile/methods/mod.js: -------------------------------------------------------------------------------- 1 | /* 2 | mod(int, int, ...) => nat 3 | mod(int, nat, ...) => nat 4 | mod(nat, int, ...) => nat 5 | mod(nat, nat, ...) => nat 6 | mod(mutez, nat, ...) => mutez 7 | mod(mutez, mutez, ...) => mutez 8 | */ 9 | 10 | const iotypes = { 11 | int: { 12 | int: 'nat' 13 | , nat: 'nat' 14 | } 15 | , nat: { 16 | int: 'nat' 17 | , nat: 'nat' 18 | } 19 | , mutez: { 20 | nat: 'mutez' 21 | , mutez: 'mutez' 22 | } 23 | } 24 | 25 | module.exports = function(core) { 26 | return function(op) { 27 | const ret = { 28 | code: [] 29 | , type: false 30 | } 31 | if (op.length < 2) { 32 | throw 'Not enough arguments, expects at least 2' 33 | } 34 | const a1 = core.compile.code(op.shift()); let an 35 | if (typeof iotypes[a1.type[0]] == 'undefined') { 36 | throw `Invalid type for mod, expects int, nat or mutez not ${a1.type[0]}` 37 | } 38 | ret.type = a1.type 39 | ret.code = a1.code 40 | while (op.length) { 41 | an = core.compile.code(op.shift()) 42 | if (typeof iotypes[ret.type[0]][an.type[0]] == 'undefined') { 43 | throw `Invalid type for mod ${an.type[0]}` 44 | } 45 | ret.type = [iotypes[ret.type[0]][an.type[0]]] 46 | ret.code.push(['DIP', an.code]) 47 | ret.code.push('EDIV') 48 | ret.code.push(['IF_NONE', ['PUSH string "Divisible by 0"', 'FAILWITH'], []]) 49 | ret.code.push('CDR') 50 | } 51 | return ret 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/compile/methods/div.js: -------------------------------------------------------------------------------- 1 | /* 2 | div(int, int, ...) => int 3 | div(int, nat, ...) => int 4 | div(nat, int, ...) => int 5 | div(nat, nat, ...) => nat 6 | div(mutez, nat, ...) => mutez 7 | div(mutez, mutez, ...) => nat 8 | */ 9 | 10 | const iotypes = { 11 | int: { 12 | int: 'int' 13 | , nat: 'int' 14 | } 15 | , nat: { 16 | int: 'int' 17 | , nat: 'nat' 18 | } 19 | , mutez: { 20 | nat: 'mutez' 21 | , mutez: 'nat' 22 | } 23 | } 24 | 25 | module.exports = function(core) { 26 | return function(op) { 27 | const ret = { 28 | code: [] 29 | , type: false 30 | } 31 | if (op.length < 2) { 32 | throw 'Not enough arguments, expecting at least two' 33 | } 34 | const a1 = core.compile.code(op.shift()); let an 35 | if (typeof iotypes[a1.type[0]] == 'undefined') { 36 | throw `Invalid type for div, expecting int, nat or mutez, not ${a1.type[0]}` 37 | } 38 | ret.type = a1.type 39 | ret.code = a1.code 40 | while (op.length) { 41 | an = core.compile.code(op.shift()) 42 | if (typeof iotypes[ret.type[0]][an.type[0]] == 'undefined') { 43 | throw `Invalid type for div between ${ret.type[0]} and ${an.type[0]}` 44 | } 45 | ret.type = [iotypes[ret.type[0]][an.type[0]]] 46 | ret.code.push(['DIP', an.code]) 47 | ret.code.push('EDIV') 48 | ret.code.push(['IF_NONE', ['PUSH string "Divisible by 0"', 'FAILWITH'], []]) 49 | ret.code.push('CAR') 50 | } 51 | return ret 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/compile/methods/add.js: -------------------------------------------------------------------------------- 1 | /* 2 | add(int, int, ...) => int 3 | add(int, nat, ...) => int 4 | add(nat, int, ...) => int 5 | add(nat, nat, ...) => nat 6 | add(mutez, mutez, ...) => mutez 7 | add(timestamp, int, ...) => timestamp 8 | add(int, timestamp, ...) => timestamp 9 | */ 10 | 11 | const iotypes = { 12 | int: { 13 | int: 'int' 14 | , nat: 'int' 15 | , timestamp: 'timestamp' 16 | } 17 | , nat: { 18 | int: 'int' 19 | , nat: 'nat' 20 | } 21 | , mutez: { 22 | mutez: 'mutez' 23 | } 24 | , timestamp: { 25 | int: 'timestamp' 26 | } 27 | } 28 | 29 | module.exports = function(core) { 30 | return function(op) { 31 | const ret = { 32 | code: [] 33 | , type: false 34 | } 35 | if (op.length < 2) { 36 | throw 'Not enough arguments for function add, expects at least 2' 37 | } 38 | const instr = core.compile.ml('add'); const a1 = core.compile.code(op.shift()); let an 39 | if (typeof iotypes[a1.type[0]] == 'undefined') { 40 | throw `Invalid type for add, expects int, nat, mutez or timestamp not ${a1.type[0]}` 41 | } 42 | ret.type = a1.type 43 | ret.code = a1.code 44 | while (op.length) { 45 | an = core.compile.code(op.shift()) 46 | if (typeof iotypes[ret.type[0]][an.type[0]] == 'undefined') { 47 | throw `Invalid type for add ${an.type[0]}` 48 | } 49 | ret.type = [iotypes[ret.type[0]][an.type[0]]] 50 | ret.code.push(['DIP', an.code]) 51 | ret.code.push('ADD') 52 | } 53 | return ret 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/compile/methods/sub.js: -------------------------------------------------------------------------------- 1 | /* 2 | sub(int, int, ...) => int 3 | sub(int, nat, ...) => int 4 | sub(nat, int, ...) => int 5 | sub(nat, nat, ...) => int 6 | sub(mutez, mutez, ...) => mutez 7 | sub(timestamp, int, ...) => timestamp 8 | sub(timestamp, timestamp, ...) => timestamp 9 | */ 10 | 11 | const iotypes = { 12 | int: { 13 | int: 'int' 14 | , nat: 'int' 15 | } 16 | , nat: { 17 | int: 'int' 18 | , nat: 'int' 19 | } 20 | , mutez: { 21 | mutez: 'mutez' 22 | } 23 | , timestamp: { 24 | int: 'timestamp' 25 | , timestamp: 'int' 26 | } 27 | } 28 | 29 | module.exports = function(core) { 30 | return function(op) { 31 | const ret = { 32 | code: [] 33 | , type: false 34 | } 35 | if (op.length < 2) { 36 | throw 'Not enough arguments for function sub, expects at least 2' 37 | } 38 | const instr = core.compile.ml('sub'); const a1 = core.compile.code(op.shift()); let an 39 | if (typeof iotypes[a1.type[0]] == 'undefined') { 40 | throw 'Invalid type for sub, expects int, nat, mutez or timestamp' 41 | } 42 | ret.type = a1.type 43 | ret.code = a1.code 44 | while (op.length) { 45 | an = core.compile.code(op.shift()) 46 | if (typeof iotypes[ret.type[0]][an.type[0]] == 'undefined') { 47 | throw `Invalid type for sub ${an.type[0]}` 48 | } 49 | ret.type = [iotypes[ret.type[0]][an.type[0]]] 50 | ret.code.push(['DIP', an.code]) 51 | ret.code.push(instr) 52 | } 53 | return ret 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/compile/methods/transfer.js: -------------------------------------------------------------------------------- 1 | /* 2 | transfer(contract _, mutez, *_) 3 | transfer(address, mutez, *_) 4 | transfer(key, mutez, *_) 5 | transfer(key_hash, mutez, *_) 6 | */ 7 | module.exports = function(core) { 8 | return function(op) { 9 | const ret = { 10 | code: [] 11 | , type: false 12 | } 13 | if (op.length < 2 || op.length > 3) { 14 | throw 'Invalid arguments for function transfer, expects 2 or 3' 15 | } 16 | const to = core.compile.code(op.shift()) 17 | const amt = core.compile.code(op.shift()); let tt = ['unit'] 18 | 19 | if (['contract', 'address', 'key', 'key_hash'].indexOf(to.type[0]) < 0) { 20 | throw `Invalid type for transfer to, expecting contract, address, key or key_hash not ${to.type[0]}` 21 | } 22 | if (amt.type[0] != 'mutez') { 23 | throw `Invalid type for amount, expecting mutez not ${amt.type[0]}` 24 | } 25 | 26 | 27 | if (op.length) { 28 | const param = core.compile.code(op.shift()) 29 | ret.code = param.code 30 | tt = param.type 31 | } 32 | else { 33 | ret.code = ['UNIT'] 34 | } 35 | 36 | ret.code.push(['DIP', amt.code]) 37 | 38 | if (to.type[0] == 'key_hash' || to.type[0] == 'key') { 39 | if (to.type[0] == 'key') { 40 | to.code.push(core.compile.ml('hash_key')) 41 | } 42 | to.code.push(core.compile.ml('implicit_account')) 43 | } 44 | else if (to.type[0] == 'address') { 45 | to.code.push(`CONTRACT ${core.compile.type(tt)}`) 46 | to.code = to.code.concat(core.compile.error('Invalid contract')) 47 | } 48 | 49 | ret.code.push(['DIIP', to.code]) 50 | ret.code.push('TRANSFER_TOKENS') 51 | ret.code = ret.code.concat(core.compile.operation()) 52 | return ret 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/compile/type.js: -------------------------------------------------------------------------------- 1 | module.exports = function(core) { 2 | return compileType = function(p) { 3 | var p = p.slice(0) 4 | if (typeof p == 'string') { 5 | p = [[p]] 6 | } 7 | if (typeof p[0] == 'string') { 8 | p = [p] 9 | } 10 | if (p.length == 0) { 11 | return 'unit' 12 | } 13 | else if (p.length == 1) { 14 | if (core.literalTypes.indexOf(p[0][0]) >= 0) { 15 | return p[0][0] 16 | } 17 | else if (p[0][0] == 'unit') { 18 | return p[0][0] 19 | } 20 | else if (core.complexTypes.indexOf(p[0][0]) >= 0) { 21 | switch (p[0][0]) { 22 | case 'map': 23 | return `(map ${compileType([p[0][1]])} ${compileType([p[0][2]])})` 24 | break 25 | case 'big_map': 26 | return `(big_map ${compileType([p[0][1]])} ${compileType([p[0][2]])})` 27 | break 28 | case 'contract': 29 | return `(contract ${compileType([p[0][1]])})` 30 | break 31 | case 'list': 32 | return `(list ${compileType([p[0][1]])})` 33 | break 34 | case 'set': 35 | return `(set ${compileType([p[0][1]])})` 36 | break 37 | case 'option': 38 | return `(option ${compileType([p[0][1]])})` 39 | break 40 | } 41 | } 42 | else { 43 | return core.compile.namedType(core.map.struct[core.helper.findInObjArray(core.map.struct, 'name', p[0][0])].type) 44 | } 45 | } 46 | else { 47 | let ret = '(pair ' 48 | ret += compileType([p.shift()]) 49 | ret += ' ' 50 | let e = '' 51 | while (p.length > 1) { 52 | ret += '(pair ' 53 | ret += compileType([p.shift()]) 54 | ret += ' ' 55 | e += ')' 56 | } 57 | ret += compileType([p.shift()]) 58 | ret += ')' 59 | ret += e 60 | return ret 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /e2e/test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs').promises 4 | const path = require('path') 5 | const compiler = require('../index') 6 | 7 | const compileFile = async file => { 8 | const filePath = path.join(process.cwd(), file) 9 | const data = await fs.readFile(filePath, { 10 | encoding: 'utf-8' 11 | }) 12 | return compiler.compile(data) 13 | } 14 | 15 | describe(`Compiler`, () => { 16 | const contracts = [ 17 | 'bigmap' 18 | , 'blank' 19 | , 'bool' 20 | , 'bytes' 21 | , 'input' 22 | , 'key' 23 | , 'key_hash' 24 | , 'list' 25 | , 'map' 26 | , 'mutez' 27 | , 'numbers' 28 | , 'optional' 29 | , 'set' 30 | , 'signature' 31 | , 'storage' 32 | , 'string' 33 | , 'timestamp' 34 | ] 35 | 36 | const blankContract = `entry Blank(){}` 37 | 38 | contracts.forEach(contract => { 39 | it(`${contract} sample compiles correctly`, async () => { 40 | const compiled = await compileFile(`e2e/fi-samples/${contract}_test.fi`) 41 | expect(compiled).toMatchSnapshot() 42 | }) 43 | }) 44 | 45 | it(`has valid default config`, () => { 46 | const defaultConfig = { 47 | abi_format: 'compact' 48 | , ml_format: 'compact' 49 | , macros: true 50 | } 51 | const {config} = compiler.compile(blankContract) 52 | expect(config).toMatchObject(defaultConfig) 53 | }) 54 | 55 | it(`throws on invalid config`, () => { 56 | expect(() => { 57 | compiler.compile(blankContract, { 58 | abi_format: 'bad' 59 | }) 60 | }) 61 | .toThrow() 62 | 63 | expect(() => { 64 | compiler.compile(blankContract, { 65 | ml_format: 'bad' 66 | }) 67 | }) 68 | .toThrow() 69 | 70 | expect(() => { 71 | compiler.compile(blankContract, { 72 | macros: 'bad' 73 | }) 74 | }) 75 | .toThrow() 76 | }) 77 | }) 78 | -------------------------------------------------------------------------------- /lib/compile/methods/new.js: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | const comparable = ['nat', 'int', 'mutez', 'string', 'timestamp', 'bytes', 'key_hash', 'bool', 'address'] 4 | module.exports = function(core) { 5 | return function(op) { 6 | const ret = { 7 | code: [] 8 | , type: false 9 | } 10 | const t = op.shift(); let t1; let t2 11 | switch (t) { 12 | case 'set': 13 | t1 = op.shift() 14 | if (comparable.indexOf(t1[0]) < 0) { 15 | throw `Invalid type for key, expects comparable type not ${t1[0]}` 16 | } 17 | ret.code = [`EMPTY_SET ${core.compile.type(t1)}`] 18 | ret.type = ['set', t1] 19 | break 20 | case 'list': 21 | t1 = op.shift() 22 | ret.code = [`NIL ${core.compile.type(t1)}`] 23 | ret.type = ['list', t1] 24 | break 25 | case 'map': 26 | t1 = op.shift() 27 | if (comparable.indexOf(t1[0]) < 0) { 28 | throw `Invalid type for key, expects comparable type not ${t1[0]}` 29 | } 30 | t2 = op.shift() 31 | ret.code = [`EMPTY_MAP ${core.compile.type(t1)} ${core.compile.type(t2)}`] 32 | ret.type = ['map', t1, t2] 33 | break 34 | default: 35 | if (core.helper.findInObjArray(core.map.struct, 'name', t) < 0) { 36 | throw `Unknown struct ${t}` 37 | } 38 | op = op.reverse() 39 | var a1 = core.compile.code(op.shift()); var an; var ct = [] 40 | ret.code = a1.code 41 | ct.unshift(a1.type) 42 | while (op.length) { 43 | an = core.compile.code(op.shift()) 44 | if (!an.type) { 45 | throw 'Invalid argument for new' 46 | } 47 | ct.unshift(an.type) 48 | ret.code.push(['DIP', an.code]) 49 | ret.code.push('SWAP') 50 | ret.code.push('PAIR') 51 | } 52 | if (core.compile.type(ct) != core.compile.type(t)) { 53 | throw `Type error with arguments for new struct ${t}` 54 | } 55 | ret.type = [t] 56 | break 57 | } 58 | return ret 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/compile/methods/push.js: -------------------------------------------------------------------------------- 1 | /* 2 | push(xx, _ key, _ val) 3 | xx.push(_ key, _ val) 4 | *Note xx is of type list, map, big_map or set 5 | */ 6 | module.exports = function(core) { 7 | return function(op) { 8 | const ret = { 9 | code: [] 10 | , type: false 11 | } 12 | const vName = op.shift() 13 | if (op.length == 2) { 14 | const map = core.compile.code(vName); const key = core.compile.code(op.shift()); var val = core.compile.code(op.shift()) 15 | if (['map', 'big_map'].indexOf(map.type[0]) < 0) { 16 | throw `Invalid type for push, expects map or bigmap not ${map.type[0]}` 17 | } 18 | if (core.compile.type(key.type) != core.compile.type(map.type[1])) { 19 | throw `Invalid type for key expects ${map.type[1]} not ${key.type}` 20 | } 21 | if (core.compile.type(val.type) != core.compile.type(map.type[2])) { 22 | throw `Invalid type for value expects ${map.type[2]} not ${val.type}` 23 | } 24 | ret.code = key.code 25 | ret.code.push(['DIP', val.code.concat(['SOME'])]) 26 | ret.code.push(['DIIP', map.code]) 27 | ret.code.push(core.compile.ml('update')) 28 | } 29 | else if (op.length == 1) { 30 | const ls = core.compile.code(vName); var val = core.compile.code(op.shift()) 31 | if (ls.type[0] == 'list') { 32 | if (core.compile.type(val.type[0]) != core.compile.type(ls.type[1])) { 33 | throw `Invalid value type for push, expects ${ls.type[1]} not ${val.type[0]}` 34 | } 35 | ret.code = val.code 36 | ret.code.push(['DIP', ls.code]) 37 | ret.code.push(core.compile.ml('cons')) 38 | } 39 | else if (ls.type[0] == 'set') { 40 | if (core.compile.type(val.type[0]) != core.compile.type(ls.type[1])) { 41 | throw `Invalid value type for push, expects ${ls.type[1]} not ${val.type[0]}` 42 | } 43 | ret.code = val.code 44 | ret.code.push(['DIP', ['PUSH bool True']]) 45 | ret.code.push(['DIIP', ls.code]) 46 | ret.code.push(core.compile.ml('update')) 47 | } 48 | else { 49 | throw `Invalid type for push, expects map, big_map, list or set not ${ls.type[0]}` 50 | } 51 | } 52 | ret.code = ret.code.concat(core.compile.setter(vName[0]).code) 53 | return ret 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/parse/type.js: -------------------------------------------------------------------------------- 1 | // This function will take in any fi type as a stirng, and produce a parsed array 2 | module.exports = function(core) { 3 | return parseType = function(t) { 4 | const to = []; let val = ''; let cc = ''; let slevel = 0 5 | for (let i = 0; i < t.length; i++) { 6 | cc = t[i] 7 | if (val == '' && cc == ' ') { 8 | continue 9 | } 10 | else if (slevel > 0) { 11 | if (cc == ']') { 12 | slevel-- 13 | } 14 | if (cc == '[') { 15 | slevel++ 16 | } 17 | if (slevel == 1 && cc == ',') { 18 | to.push(val) 19 | val = '' 20 | continue 21 | } 22 | if (slevel == 1 && cc == '=' && t[i + 1] == '>') { 23 | to.push(val) 24 | val = '' 25 | i++ 26 | continue 27 | } 28 | else if (slevel == 0) { 29 | if (val == '' && to.length == 1) { 30 | to.unshift('list') 31 | continue 32 | } 33 | else { 34 | to.push(val) 35 | val = '' 36 | continue 37 | } 38 | } 39 | val += cc 40 | } 41 | else { 42 | if (cc == '?') { 43 | to.push('option') 44 | val = '' 45 | continue 46 | } 47 | else if (cc == '[') { 48 | slevel++ 49 | to.push(val) 50 | val = '' 51 | continue 52 | } 53 | else if (cc == ' ') { 54 | to.push(val) 55 | val = '' 56 | continue 57 | } 58 | val += cc 59 | } 60 | } 61 | if (val != '') { 62 | to.push(val) 63 | } 64 | const mt = to.shift() 65 | let ret = '' 66 | switch (mt) { 67 | case 'tz1': 68 | case 'pkh': 69 | ret = ['key_hash'] 70 | break 71 | case 'string': 72 | case 'int': 73 | case 'nat': 74 | case 'bool': 75 | case 'timestamp': 76 | case 'mutez': 77 | case 'address': 78 | case 'operation': 79 | case 'key': 80 | case 'key_hash': 81 | case 'signature': 82 | case 'bytes': 83 | ret = [mt] 84 | break 85 | case 'map': 86 | ret = ['map', parseType(to[0]), parseType(to[1])] 87 | break 88 | case 'bmap': 89 | ret = ['big_map', parseType(to[0]), parseType(to[1])] 90 | break 91 | case 'contract': 92 | ret = ['contract', parseType(to[0])] 93 | break 94 | case 'list': 95 | ret = ['list', parseType(to[0])] 96 | break 97 | case 'option': 98 | ret = ['option', parseType(to[0])] 99 | break 100 | case 'set': 101 | ret = ['set', parseType(to[0])] 102 | break 103 | default: 104 | ret = [mt] 105 | break 106 | } 107 | return ret 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /lib/parse/condition.js: -------------------------------------------------------------------------------- 1 | module.exports = function(core) { 2 | return parseCondition = function(cond) { 3 | cond = cond.replace(/!=/g, 'neq') 4 | cond = cond.replace(/<=/g, 'le') 5 | cond = cond.replace(/>=/g, 'ge') 6 | cond = cond.replace(//g, 'gt') 8 | cond = cond.replace(/==/g, 'eq') 9 | cond = cond.replace(/&&/g, 'and') 10 | cond = cond.replace(/\|\|/g, 'or') 11 | let escaped = false; let instring = false; const fna = []; let cfn = ''; const condArray = []; const current = []; var ret = ''; let cc = ''; let val = ''; let co = []; let cos = []; var ret = ''; const cmp = ''; let clevel = 0; const lhs = false; let cs = []; let op 12 | for (let i = 0; i < cond.length; i++) { 13 | cc = cond[i] 14 | if (instring) { 15 | if (escaped) { 16 | escaped = false 17 | } 18 | else if (cc == '\\') { 19 | escaped = true 20 | } 21 | else if (cc == '"') { 22 | cs.push(val) 23 | instring = false 24 | val = '' 25 | continue 26 | } 27 | val += cc 28 | continue 29 | } 30 | else if (val == '' && (/\s/).test(cc)) { 31 | continue 32 | } 33 | else if (clevel > 0) { 34 | if (cc == ')') { 35 | clevel-- 36 | } 37 | if (cc == '(') { 38 | clevel++ 39 | } 40 | if (clevel == 0) { 41 | if (cfn) { 42 | cs = (core.parse.main(`${cfn}(${val})`)) 43 | cfn = '' 44 | } 45 | else { 46 | cs = parseCondition(val) 47 | } 48 | val = '' 49 | continue 50 | } 51 | val += cc 52 | } 53 | else if (cc == '"') { 54 | instring = true 55 | if (val.trim() != '') { 56 | cs.push(val.trim()) 57 | } 58 | val = '' 59 | continue 60 | } 61 | else if (cc == '!') { 62 | co.unshift('neq') 63 | continue 64 | } 65 | else { 66 | if (cc == '(') { 67 | if (val) { 68 | cfn = val 69 | } 70 | val = '' 71 | clevel++ 72 | continue 73 | } 74 | else if (cc == ' ') { 75 | if (['and', 'or'].indexOf(val) >= 0) { 76 | if (cs.length && co.length) { 77 | co.push(cs) 78 | } 79 | if (cs.length && !co.length) { 80 | co = cs 81 | } 82 | if (cos.length) { 83 | cos.push(co) 84 | cos = [val, cos] 85 | } 86 | else { 87 | cos = [val, co] 88 | } 89 | co = [] 90 | cs = [] 91 | } 92 | else 93 | if (['neq', 'le', 'ge', 'lt', 'gt', 'eq'].indexOf(val) >= 0) { 94 | co = [val, cs] 95 | cs = [] 96 | } 97 | else { 98 | cs.push(val) 99 | } 100 | val = '' 101 | continue 102 | } 103 | val += cc 104 | } 105 | } 106 | if (val != '') { 107 | cs.push(val) 108 | } 109 | if (cs.length) { 110 | if (co.length) { 111 | co.push(cs) 112 | } 113 | else { 114 | cos.push(cs) 115 | } 116 | } 117 | if (co.length) { 118 | cos.push(co) 119 | } 120 | if (cos.length == 1) { 121 | cos = cos[0] 122 | } 123 | if (['neq', 'le', 'ge', 'lt', 'gt', 'eq', 'and', 'or'].indexOf(cos[0]) < 0) { 124 | cos = ['eq', cos] 125 | } 126 | return cos 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fi - Smart Contract language for Tezos 2 | 3 | fi (pronounced fee) is a powerful, smart contract language for Tezos that compiles down to valid and verifiable Michelson code. fi is currently in early alpha stages - please use at your own risk, and validate any compiled code. 4 | 5 | [Check us out online](https://fi-code.com), or read through our [Documentation](https://learn.fi-code.com/) to get started. 6 | 7 | ## Installation 8 | 9 | ### Node.js 10 | 11 | You can install the npm plugin: 12 | 13 | ```npm install fi-compiler``` 14 | 15 | You can the include the installed module: 16 | 17 | ```javascript 18 | var fi = require("fi-compiler"); 19 | ``` 20 | 21 | ### Web 22 | 23 | You can also build a distributable version to be included on the web: 24 | 25 | ```npm run build``` 26 | 27 | This will generate a file within the dist directory. You can download and use the currently built dist file as well. 28 | 29 | ```html 30 | 31 | ``` 32 | 33 | This will expose a global function, fi: 34 | 35 | ```javascript 36 | console.log(fi); 37 | ``` 38 | 39 | ### CLI 40 | 41 | You can also install our npm CLI plugin, which allows you to compile fi directly from the command line: 42 | 43 | ```npm i -g fi-cli``` 44 | 45 | You can then compile code directly from your terminal using: 46 | 47 | ```bash 48 | fi compile test_contract.fi 49 | ``` 50 | This will output two files in the same directory as the target fi file: 51 | xx.fi.abi => The contract ABI 52 | xx.fi.ml => The compuled michelson code 53 | 54 | 55 | ## Usage 56 | 57 | You can use the following exposed functions when using the web or node.js library: 58 | 59 | ### fi.version 60 | 61 | This returns the current version number of the fi-compiler. 62 | 63 | ```console.log(fi.version);``` 64 | 65 | ### fi.compile(code, config) 66 | 67 | This takes one input argument, a string containing fi compile. This returns an object with two elements, code and abi. 68 | 69 | ```javascript 70 | var ficode = ` 71 | storage string name; 72 | entry changeName(string name ){ 73 | storage.name = input.name; 74 | } 75 | `; 76 | var compiled = fi.compile(ficode); 77 | console.log(compiled.ml); //Michelson code 78 | console.log(compiled.abi); //ABI JSON string 79 | ``` 80 | 81 | You can also parse a config object, which is optional. The default settings are: 82 | 83 | - ml_format - defaults to "optimized" for minimal michelson, can set as "readable" for a human readable version, or "array" for a JS array 84 | - abi_format - defaults to "optimized" for the bare minimum, can be set as "full" for a full ABI copy including an array representation of the fi code 85 | - macros - WIP 86 | 87 | ### fi.abi.load(abi) 88 | 89 | You can manually loan an ABI string, which can be used to build input parameters for contract calls. In future, we aim to add additional support for more helper functions. 90 | 91 | ```javascript 92 | fi.abi.load(compiled.abi); 93 | ``` 94 | 95 | ### fi.abi.entry(entry, input) 96 | 97 | This function allows you to convert a JSON input object into packed bytes for calling a contract using a loaded ABI file. This function takes two input arguments, the name of the function you are calling, and the JSON object with the input. 98 | 99 | ```javascript 100 | var input = { 101 | name : "Stephen" 102 | }; 103 | console.log(fbi.abi.entry("changeName", input)); // Returns packed bytes for a contract call 104 | ``` 105 | ``` 106 | -------------------------------------------------------------------------------- /lib/compile/code.js: -------------------------------------------------------------------------------- 1 | module.exports = function(core) { 2 | const 3 | varModifiers = ['add', 'sub', 'mul', 'div', 'mod', 'concat'] 4 | const varFunctions = ['in', 'length', 'get', 'push', 'drop', 'pop', 'to_address', 'to_contract', 'to_nat', 'to_int', 'to_optional', 'to_pkh', 'to_some', 'to_mutez'] 5 | const methodHandlers = {} 6 | const constants = { 7 | BALANCE: { 8 | code: ['BALANCE'] 9 | , type: 'mutez' 10 | } 11 | , SOURCE: { 12 | code: ['SOURCE'] 13 | , type: 'address' 14 | } 15 | , SENDER: { 16 | code: ['SENDER'] 17 | , type: 'address' 18 | } 19 | , SELF: { 20 | code: ['SELF'] 21 | , type: 'contract *' 22 | } 23 | , OWNER: { 24 | code: ['SELF', 'ADDRESS'] 25 | , type: 'address' 26 | } 27 | , AMOUNT: { 28 | code: ['AMOUNT'] 29 | , type: 'mutez' 30 | } 31 | , GAS: { 32 | code: ['STEPS_TO_QUOTA'] 33 | , type: 'nat' 34 | } 35 | , NOW: { 36 | code: ['NOW'] 37 | , type: 'timestamp' 38 | } 39 | } 40 | const methods = ['abs', 'add', 'assert', 'concat', 'concat_ws', 'delegate', 'div', 'drop', 'get', 'hash', 'if', 'in', 'isset', 'length', 'mod', 'mul', 'neg', 'new', 'none', 'pack', 'pop', 'push', 'set', 'slice', 'sqr', 'sub', 'throw', 'to_address', 'to_contract', 'to_int', 'to_mutez', 'to_nat', 'to_optional', 'to_pkh', 'to_some', 'transfer', 'unpack', 'verify'] 41 | 42 | for (let i = 0; i < methods.length; i++) { 43 | methodHandlers[methods[i]] = require(`./methods/${methods[i]}`)(core) 44 | } 45 | const _compileCode = function(op) { 46 | var op = op.slice(0); const code = ''; let com = op.shift() 47 | if (com == 'ext') { 48 | com = op.shift() 49 | if (varFunctions.indexOf(com) >= 0) { 50 | var n = op.shift() 51 | op.unshift([n]) 52 | } 53 | else if (varModifiers.indexOf(com) >= 0) { 54 | var n = op.shift() 55 | op = [n, [com, [n]].concat(op.slice(0))] 56 | com = 'set' 57 | } 58 | else { 59 | throw `Unknown variable modifier/function ${com}` 60 | } 61 | } 62 | 63 | if (core.literalTypes.indexOf(com) >= 0) { 64 | if (com == 'pkh') { 65 | com = 'key_hash' 66 | } 67 | return { 68 | code: [`PUSH ${com} ${core.compile.literal(com, op.shift())}`] 69 | , type: [com] 70 | } 71 | } 72 | else if (typeof constants[com] != 'undefined') { 73 | return { 74 | code: constants[com].code.slice(0) 75 | , constant: com 76 | , type: [constants[com].type] 77 | } 78 | } 79 | else if (typeof core.map.const != 'undefined' && core.helper.findInObjArray(core.map.const, 'name', com) >= 0) { 80 | const i = core.helper.findInObjArray(core.map.const, 'name', com) 81 | return { 82 | code: [`PUSH ${core.map.const[i].type} ${core.compile.literal(core.map.const[i].type, core.map.const[i].value)}`] 83 | , type: [core.map.const[i].type] 84 | } 85 | } 86 | else if (typeof methodHandlers[com] != 'undefined') { 87 | const t = methodHandlers[com](op) 88 | return t 89 | } 90 | 91 | try { 92 | return core.compile.getter(com) 93 | } 94 | catch (e) { 95 | throw `Unexpected command ${com}` 96 | } 97 | } 98 | 99 | return function(code) { 100 | if (typeof code == 'string') { 101 | code = core.parse.main(code) 102 | } 103 | if (typeof code[0] == 'string') { 104 | return _compileCode(code) 105 | } 106 | let compiled = [] 107 | for (let i = 0; i < code.length; i++) { 108 | op = code[i].slice(0) 109 | if (!op.length) { 110 | continue 111 | } 112 | try { 113 | var line = _compileCode(op) 114 | } 115 | catch (e) { 116 | throw e 117 | } 118 | // if (typeof line.code != 'string') throw "Invalid line function"; 119 | if (line.type) { 120 | throw 'Code returns a value that is not stored' 121 | } 122 | compiled = compiled.concat(line.code) 123 | } 124 | return { 125 | code: compiled 126 | , type: false 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /e2e/fi-samples/all_test.fi: -------------------------------------------------------------------------------- 1 | storage bmap[nat=>address] bmaptest; 2 | storage nat test; 3 | 4 | entry TestInput(nat test1){ 5 | storage.test = input.test1; 6 | } 7 | entry TestKey(){ 8 | let key test1 = key "edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym"; 9 | let pkh test2 = to_pkh(key "edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym"); 10 | let address test3 = to_address(key "edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym"); 11 | let contract[unit] test4 = to_contract(test1); 12 | let address test5 = to_address(test4); 13 | 14 | transfer(test1, mutez 0); 15 | transfer(test2, mutez 0); 16 | transfer(test3, mutez 0); 17 | transfer(test4, mutez 0); 18 | transfer(test5, mutez 0); 19 | 20 | let bool test6 = verify(bytes 0x0500, signature "edsigtgjPezMVW9zCu8ny4uMaAsT3gqbRANSNAzVBvZoFXx3ZiPifbLUUthdj4tewcCvJf3HpPfMTnML73hCi6UnFvZpvWsqTBk", test1); 21 | } 22 | entry TestSignature(){ 23 | let bool test1 = verify(bytes 0x0500, signature "edsigtgjPezMVW9zCu8ny4uMaAsT3gqbRANSNAzVBvZoFXx3ZiPifbLUUthdj4tewcCvJf3HpPfMTnML73hCi6UnFvZpvWsqTBk", key "edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym"); 24 | } 25 | entry TestPkh(){ 26 | let pkh test1 = pkh "tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM"; 27 | let address test2 = to_address(pkh "tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM"); 28 | let contract[unit] test3 = to_contract(test1); 29 | 30 | transfer(test1, mutez 0); 31 | transfer(test2, mutez 0); 32 | transfer(test3, mutez 0); 33 | 34 | delegate(); 35 | delegate(test1); 36 | delegate(pkh "tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM"); 37 | } 38 | entry TestString(){ 39 | let string test1 = string "Hello World"; 40 | let string test2 = concat(string "Hello World", test1); 41 | let nat test3 = test1.length(); 42 | let string test4 = slice(test2, nat 0, nat 10); 43 | } 44 | entry TestNumbers(){ 45 | let nat test1 = nat 100; 46 | let nat test2 = add(test1, nat 2, nat 3); 47 | let nat test3 = to_nat(sub(add(to_int(mutez 0), int 2), int 3, nat 5)); 48 | let int test4 = to_int(mutez 0); 49 | let nat test5 = to_nat(sub(add(to_int(mutez 0), int 2), int 3, nat 5)); 50 | let nat test6 = abs(to_int(test2)); 51 | } 52 | entry TestTimestamp(){ 53 | let timestamp test1 = NOW; 54 | let timestamp test2 = add(test1, int 100); 55 | let int test3 = sub(test2, test1); 56 | let timestamp test4 = add(int 200, NOW, sub(NOW, test1)); 57 | } 58 | entry TestMutez(){ 59 | let mutez test1 = AMOUNT; 60 | let mutez test2 = add(test1, AMOUNT, BALANCE); 61 | } 62 | entry TestBool(){ 63 | let bool test1 = bool True; 64 | if (test1 == bool False){ 65 | assert(bool True); 66 | assert(test1); 67 | } 68 | } 69 | entry TestList(){ 70 | let address[] test1 = new list(address); 71 | test1.push(SENDER); 72 | let address test2 = test1.pop(); 73 | let nat test3 = test1.length(); 74 | } 75 | entry TestSet(){ 76 | let set[nat] test1 = new set(nat); 77 | test1.push(nat 100); 78 | test1.push(nat 200); 79 | test1.push(nat 300); 80 | let nat test2 = nat 100; 81 | if (test1.in(test2)){ 82 | test1.drop(test2); 83 | } 84 | let nat test3 = test1.length(); 85 | } 86 | entry TestMap(){ 87 | let map[nat=>address] test1 = new map(nat, address); 88 | test1.push(nat 1, SENDER); 89 | test1.push(nat 2, address "tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM"); 90 | let address test2 = test1.get(nat 1); 91 | if (test1.in(nat 1)){ 92 | test1.drop(nat 2); 93 | } 94 | let nat test3 = test1.length(); 95 | } 96 | entry TestBigmap(){ 97 | storage.bmaptest.push(nat 1, SENDER); 98 | storage.bmaptest.push(nat 2, address "tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM"); 99 | let address test2 = storage.bmaptest.get(nat 1); 100 | if (storage.bmaptest.in(nat 1)){ 101 | storage.bmaptest.drop(nat 2); 102 | } 103 | } 104 | entry TestOptional(){ 105 | let ?nat test1 = to_optional(nat 1); 106 | let bool test2 = isset(test1); 107 | let nat = to_some(test1); 108 | } 109 | entry TestBytes(){ 110 | let bytes test1 = bytes 0x0500; 111 | test1 = concat(test1, bytes 0x050001); 112 | let bytes test2 = hash(bytes 0x050001); 113 | let bytes test3 = hash(bytes 0x050001, blake2b); 114 | let bytes test4 = hash(bytes 0x050001, sha256); 115 | let bytes test5 = hash(bytes 0x050001, sha512); 116 | let bytes test6 = pack(nat 1); 117 | let nat test7 = unpack(test3, nat); 118 | let nat test8 = test6.length(); 119 | let bytes test9 = slice(test2, nat 0, nat 4); 120 | } -------------------------------------------------------------------------------- /lib/compile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(core) { 2 | var _find = function(v, t) { 3 | var v = v.splice(0) 4 | const n = v.shift() 5 | const c = core.helper.findInObjArray(t, 'name', n) 6 | if (c < 0) { 7 | throw `Error with variable ${n}` 8 | } 9 | const cm = t.length 10 | if (v.length > 0) { 11 | nt = t[c].type 12 | const rinner = _find(v, core.map.struct[core.helper.findInObjArray(core.map.struct, 'name', nt[0])].type) 13 | const rt = rinner.ind.slice(0) 14 | rt.push([c, cm]) 15 | return { 16 | ind: rt 17 | , type: rinner.type 18 | } 19 | } 20 | return { 21 | ind: [[c, cm]] 22 | , type: t[c].type 23 | } 24 | } 25 | const pairer = function(c, cm) { 26 | let r = '' 27 | if (c == 0 && cm == 1) { 28 | return r 29 | } 30 | r += 'D'.repeat(c) 31 | if (c < (cm - 1)) { 32 | r += 'A' 33 | } 34 | return r 35 | } 36 | const find = function(v) { 37 | const originalV = v; var v = v.split('.'); let tt; let t; let n; let rettype 38 | if (['storage', 'input'].indexOf(v[0]) >= 0) { 39 | t = v.shift() 40 | } 41 | else { 42 | t = 'temp' 43 | } 44 | if (t == 'storage') { 45 | tt = core.map.storage 46 | } 47 | else { 48 | tt = core.map.entry[core.currentCall][t] 49 | } 50 | if (v.length > 0) { 51 | try { 52 | const dd = _find(v, tt); var ind = dd.ind.reverse() 53 | rettype = dd.type 54 | } 55 | catch (e) { 56 | throw `Error: ${e} - for variable ${originalV}` 57 | } 58 | var c = 'C'; var a = '' 59 | for (let i = 0; i < ind.length; i++) { 60 | a += pairer(ind[i][0], ind[i][1]) 61 | } 62 | } 63 | else { 64 | var c = 'C'; var a = '' 65 | rettype = [t] 66 | } 67 | if (t == 'temp') { 68 | c += 'A' 69 | c += a 70 | c += 'R' 71 | return { 72 | code: c 73 | , type: rettype 74 | } 75 | } 76 | if (core.map.entry[core.currentCall].temp.length > 0) { 77 | c += 'D' 78 | } 79 | if (t == 'input') { 80 | c += 'A' 81 | c += a 82 | c += 'R' 83 | return { 84 | code: c 85 | , type: rettype 86 | } 87 | } 88 | if (core.map.entry[core.currentCall].input.length > 0) { 89 | c += 'D' 90 | } 91 | if (t == 'storage') { 92 | c += 'D' 93 | c += a 94 | c += 'R' 95 | return { 96 | code: c 97 | , type: rettype 98 | } 99 | } 100 | throw 'error' 101 | } 102 | 103 | return { 104 | script: require('./compile/script')(core) 105 | , code: require('./compile/code')(core) 106 | , condition: require('./compile/condition')(core) 107 | , type: require('./compile/type')(core) 108 | , rawType(t) { 109 | return core.compile.type(core.parse.type(t)) 110 | } 111 | , namedType: require('./compile/namedType')(core) 112 | 113 | // Helpers 114 | , operation() { 115 | let c = 'C' 116 | if (core.map.entry[core.currentCall].temp.length > 0) { 117 | c += 'D' 118 | } 119 | if (core.map.entry[core.currentCall].input.length > 0) { 120 | c += 'D' 121 | } 122 | c += 'AR' 123 | return [['DIP', ['DUP', c]], 'CONS', 'SWAP', `SET_${c}`] 124 | } 125 | , ml(a, b) { 126 | let code = a.toUpperCase() 127 | if (typeof b != 'undefined') { 128 | code = [a, b] 129 | } 130 | return code 131 | } 132 | , literal(t, v) { 133 | if (['bool'].indexOf(t) >= 0) { 134 | if (v.toLowerCase() === 'true') { 135 | return 'True' 136 | } 137 | else if (v.toLowerCase() === 'false') { 138 | return 'False' 139 | } 140 | throw 'Invalid value for bool' 141 | } 142 | else if (['set', 'map', 'big_map', 'contract', 'list', 'option'].indexOf(t) >= 0) { 143 | return v 144 | } 145 | else if (['bytes'].indexOf(t) >= 0) { 146 | return v 147 | } 148 | else if (['nat', 'int', 'mutez'].indexOf(t) >= 0) { 149 | if (isNaN(v)) { 150 | throw `NaN error: ${v}` 151 | } 152 | return parseInt(v) 153 | } 154 | else if (['string'].indexOf(t) >= 0) { 155 | return `"${v}"` 156 | } 157 | else if (['address', 'key', 'key_hash', 'signature'].indexOf(t) >= 0) { 158 | if (v.substr(0, 2) == '0x') { 159 | return v 160 | } 161 | return `"${v}"` 162 | } 163 | else if (['timestamp'].indexOf(t) >= 0) { 164 | if (!isNaN(v)) { 165 | return `"${v}"` 166 | } 167 | return parseInt(v) 168 | } 169 | } 170 | , error(e) { 171 | if (typeof e == 'string') { 172 | e = `string "${e}"` 173 | } 174 | else { 175 | e = `nat ${parseInt(e)}` 176 | } 177 | return [['IF_NONE', [`PUSH ${e}`, 'FAILWITH'], []]] 178 | } 179 | , getter(v) { 180 | const f = find(v) 181 | return { 182 | code: ['DUP', f.code] 183 | , type: f.type 184 | } 185 | } 186 | , setter(v) { 187 | const f = find(v) 188 | return { 189 | code: ['SWAP', `SET_${f.code}`] 190 | , type: f.type 191 | } 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /lib/parse/main.js: -------------------------------------------------------------------------------- 1 | module.exports = function(core) { 2 | var parse = function(script) { 3 | let escaped = false; let tokens = []; let token = []; let cc = ''; let val = ''; let instring = false; let incomment = false; let cl = 0; let bl = 0; let sl = 0; let append = false; let currentLine = 1; let currentChar = 1 4 | function throwError(e) { 5 | throw `${e} at line ${currentLine}, character ${currentChar}` 6 | } 7 | for (let i = 0; i < script.length; i++) { 8 | cc = script[i] 9 | if (cc == '\n') { 10 | currentLine++ 11 | currentChar = 1 12 | } 13 | currentChar++ 14 | if (incomment) { 15 | if (cc == '\n') { 16 | incomment = false 17 | } 18 | continue 19 | } 20 | else if (instring) { 21 | if (!escaped && cc == '"') { 22 | token.push(val) 23 | instring = false 24 | val = '' 25 | continue 26 | } 27 | else if (escaped) { 28 | escaped = false 29 | } 30 | else if (cc == '\\') { 31 | escaped = true 32 | } 33 | } 34 | else if (val == '' && (/\s/).test(cc)) { 35 | continue 36 | } 37 | else if (sl > 0) { 38 | if (cc == ']') { 39 | sl-- 40 | } 41 | if (cc == '[') { 42 | sl++ 43 | } 44 | if (sl == 0) { 45 | var t = token.pop() 46 | t += `[${val.trim()}]` 47 | token.push(t) 48 | val = '' 49 | continue 50 | } 51 | } 52 | else if (cl > 0) { 53 | if (cc == ')') { 54 | cl-- 55 | } 56 | if (cc == '(') { 57 | cl++ 58 | } 59 | if (cl == 0 || (cl == 1 && cc == ',')) { 60 | if (['if', 'assert'].indexOf(token[0]) >= 0) { 61 | if (cc == ',') { 62 | throwError('Syntax error in condition') 63 | } 64 | token.push(core.parse.condition(val.trim())) 65 | } 66 | else if (val.trim() != '') { 67 | token.push(parse(val.trim())) 68 | } 69 | else if (cc == ',') { 70 | throwError('Syntax error') 71 | } 72 | val = '' 73 | continue 74 | } 75 | } 76 | else if (bl > 0) { 77 | if (cc == '}') { 78 | bl-- 79 | } 80 | if (cc == '{') { 81 | bl++ 82 | } 83 | if (bl == 0) { 84 | token.push(parse(val.trim())) 85 | tokens.push(token) 86 | token = [] 87 | val = '' 88 | continue 89 | } 90 | } 91 | else if (cc == '#') { 92 | if (val.trim() != '') { 93 | token.push(val.trim()) 94 | } 95 | val = '' 96 | incomment = true 97 | continue 98 | } 99 | else if (cc == '"') { 100 | instring = true 101 | if (val.trim() != '') { 102 | token.push(val.trim()) 103 | } 104 | val = '' 105 | continue 106 | } 107 | else if (cc == '[') { 108 | sl = 1 109 | if (val.trim() != '') { 110 | token.push(val.trim()) 111 | } 112 | val = '' 113 | continue 114 | } 115 | else if (cc == '{') { 116 | bl = 1 117 | if (val.trim() != '') { 118 | if (val.trim() == 'else') { 119 | token = tokens.pop() 120 | } 121 | else { 122 | token.push(val.trim()) 123 | } 124 | } 125 | val = '' 126 | continue 127 | } 128 | else if (cc == '(') { 129 | cl = 1 130 | if (val.trim() != '') { 131 | if (val.indexOf('.') > 0) { 132 | const vv = val.split('.') 133 | token.unshift(vv.pop()) 134 | token.unshift('ext') 135 | val = vv.join('.') 136 | token.push(val.trim()) 137 | } 138 | else { 139 | token.push(val.trim()) 140 | } 141 | } 142 | val = '' 143 | continue 144 | } 145 | else if (cc == '=') { 146 | if (val.trim() != '') { 147 | token.push(val.trim()) 148 | } 149 | if (token[0] != 'let') { 150 | token.unshift('set') 151 | } 152 | tokens.push(token) 153 | token = [] 154 | append = true 155 | val = '' 156 | continue 157 | } 158 | else if (cc == ',') { 159 | token.push(val.trim()) 160 | val = '' 161 | continue 162 | } 163 | else if (cc == ' ') { 164 | if (val == 'else') { 165 | token = tokens.pop() 166 | } 167 | else { 168 | token.push(val.trim()) 169 | } 170 | val = '' 171 | continue 172 | } 173 | else if (cc == ';') { 174 | if (val != '') { 175 | token.push(val.trim()) 176 | } 177 | if (token.length) { 178 | if (append) { 179 | var t = tokens.pop() 180 | t.push(token) 181 | tokens.push(t) 182 | append = false 183 | } 184 | else { 185 | tokens.push(token) 186 | } 187 | } 188 | token = [] 189 | val = '' 190 | continue 191 | } 192 | val += cc 193 | } 194 | if (instring) { 195 | throw 'Syntax error, string not closed' 196 | } 197 | if (sl > 0) { 198 | throw 'Unclosed square bracket' 199 | } 200 | if (cl > 0) { 201 | throw 'Unclosed parenthesis' 202 | } 203 | if (bl > 0) { 204 | throw 'Unclosed curly bracket' 205 | } 206 | if (val.trim()) { 207 | token.push(val.trim()) 208 | } 209 | if (token.length) { 210 | tokens.push(token) 211 | } 212 | if (tokens.length == 1) { 213 | tokens = tokens[0] 214 | } 215 | return tokens 216 | } 217 | return parse 218 | } 219 | -------------------------------------------------------------------------------- /lib/compile/script.js: -------------------------------------------------------------------------------- 1 | function formatMl(mla, ti, pretty) { 2 | if (typeof pretty == 'undefined') { 3 | pretty = false 4 | } 5 | if (pretty) { 6 | var bp = ' '; var sce = ' ; ' 7 | } 8 | else { 9 | var bp = ''; var sce = ';' 10 | } 11 | var sce = ';' 12 | var mla = mla.slice(0) 13 | const ret = []; let cl 14 | for (let i = 0; i < mla.length; i++) { 15 | if (typeof mla[i] == 'string') { 16 | cl = mla[i] 17 | } 18 | else if (mla[i].length > 1) { 19 | cl = mla[i].shift() 20 | cl += `${bp}{${bp}` 21 | const cll = cl.length 22 | cl += formatMl(mla[i].shift(), ti + cll, pretty) 23 | cl += `${bp}}${bp}` 24 | if (mla[i].length) { 25 | if (pretty) { 26 | cl += `\n${(' ').repeat(ti + cll - 3)}` 27 | } 28 | cl += `${bp}{${bp}` 29 | cl += formatMl(mla[i].shift(), ti + cll, pretty) 30 | cl += `${bp}}${bp}` 31 | } 32 | } 33 | ret.push(cl) 34 | } 35 | if (pretty) { 36 | return ret.join(`${sce}\n${(' ').repeat(ti)}`) 37 | } 38 | return ret.join(sce) 39 | } 40 | function formatRemoveMacros(ml) { 41 | ml = ml.replace(/IFCMP(EQ|NEQ|LT|GT|LE|GE){/g, 'COMPARE;$1;IF{') 42 | return ml 43 | } 44 | 45 | module.exports = function(core) { 46 | return function(s, config) { 47 | const definitions = ['const', 'struct', 'storage', 'entry', 'function']; let allowDefault = false 48 | let tokens = (typeof s == 'string' ? core.parse.main(s) : s); let token; let def; let tokenMap 49 | if (typeof tokens[0] == 'string') { 50 | tokens = [tokens] 51 | } 52 | core.map = {}, core.currentCall = '' 53 | // Process definitions 54 | for (var i = 0; i < tokens.length; i++) { 55 | token = tokens[i].slice(0) 56 | def = token.shift() 57 | if (definitions.indexOf(def) < 0) { 58 | throw `Unexpected definition at root level: ${def}` 59 | } 60 | if (typeof core.map[def] == 'undefined') { 61 | core.map[def] = [] 62 | } 63 | switch (def) { 64 | case 'const': 65 | var constant = {} 66 | constant.type = token.shift(), constant.name = token.shift(), constant.value = token.shift() 67 | core.map[def].push(constant) 68 | break 69 | case 'struct': 70 | var name = token.shift() 71 | core.map[def].push({ 72 | name 73 | , type: core.parse.typeList(token) 74 | }) 75 | break 76 | case 'storage': 77 | var type = token.shift(); var name = token.shift() 78 | core.map[def].push({ 79 | name 80 | , type: core.parse.type(type) 81 | }) 82 | break 83 | case 'entry': 84 | var n = token.shift() 85 | if (n == 'default') { 86 | allowDefault = true 87 | continue 88 | } 89 | var c = token.pop(); var t = []; var tid = []; var ni 90 | while (token.length) { 91 | ni = token.shift() 92 | t.push(ni) 93 | tid.push(ni[0]) 94 | } 95 | var v = []; var cc = [] 96 | if (typeof c[0] == 'string') { 97 | c = [c] 98 | } 99 | for (let ii = 0; ii < c.length; ii++) { 100 | if (c[ii][0] == 'let') { 101 | c[ii].shift() 102 | const tt = c[ii].shift(); const nn = c[ii][0] 103 | v.push({ 104 | name: nn 105 | , type: core.parse.type(tt) 106 | }) 107 | if (c[ii].length > 1) { 108 | c[ii].unshift('set') 109 | } 110 | else { 111 | continue 112 | } 113 | } 114 | cc.push(c[ii]) 115 | } 116 | core.map[def].push({ 117 | name: n 118 | , id: `0x${core.sha256(`${n}(${tid.join(',')})`).substr(0, 8)}` 119 | , input: core.parse.typeList(t) 120 | , temp: v 121 | , code: cc 122 | }) 123 | 124 | break 125 | case 'function': 126 | throw 'Function is not currently supported' 127 | break 128 | } 129 | } 130 | if (typeof core.map.entry == 'undefined') { 131 | throw 'No defined entry calls' 132 | } 133 | 134 | if (core.map.entry.length <= 0) { 135 | throw 'No defined entry calls' 136 | } 137 | let mlstorage; const mlcode = [] 138 | if (typeof core.map.storage != 'undefined') { 139 | mlstorage = core.compile.namedType(core.map.storage) 140 | } 141 | else { 142 | mlstorage = 'unit' 143 | } 144 | 145 | for (let ci = 0; ci < core.map.entry.length; ci++) { 146 | core.currentCall = ci 147 | let code = [] 148 | if (core.map.entry[core.currentCall].input.length == 0) { 149 | code.push('DROP') 150 | } 151 | else { 152 | code.push('DUP') 153 | code.push('SIZE') 154 | code.push('PUSH nat 4') 155 | code.push('SWAP') 156 | code.push('SUB') 157 | code.push('DUP') 158 | code.push('GT') 159 | code.push(['IF', [], ['PUSH nat 102', 'FAILWITH']]) 160 | code.push('ABS') 161 | code.push('PUSH nat 4') 162 | code.push('SLICE') 163 | code = code.concat(core.compile.error(101)) 164 | 165 | code.push(`UNPACK ${core.compile.namedType(core.map.entry[core.currentCall].input)}`) 166 | code = code.concat(core.compile.error(103)) 167 | code.push('PAIR') 168 | } 169 | 170 | if (core.map.entry[core.currentCall].temp.length != 0) { 171 | const nvs = [] 172 | for (var i = 0; i < core.map.entry[core.currentCall].temp.length; i++) { 173 | nvs.unshift(core.compile.namedType([core.map.entry[core.currentCall].temp[i]])) 174 | } 175 | code.push(`NONE ${nvs[0]}`) 176 | if (nvs.length > 1) { 177 | for (i = 1; i < nvs.length; i++) { 178 | code.push(`NONE ${nvs[i]}`) 179 | code.push('PAIR') 180 | } 181 | } 182 | code.push('PAIR') 183 | } 184 | var cc = core.compile.code(core.map.entry[core.currentCall].code) 185 | code = code.concat(cc.code) 186 | 187 | // End of code, revert back to list operation:storage 188 | let ee = '' 189 | if (core.map.entry[core.currentCall].input.length) { 190 | ee += 'D' 191 | } 192 | if (core.map.entry[core.currentCall].temp.length) { 193 | ee += 'D' 194 | } 195 | if (ee) { 196 | code.push(`C${ee}R`) 197 | } 198 | mlcode.push(code) 199 | } 200 | 201 | // construct final michelson 202 | let ml = '' 203 | const mlarray = []; let cmlarray = [] 204 | cmlarray.push('parameter bytes') 205 | cmlarray.push(`storage ${mlstorage}`) 206 | cmlarray.push(['code']) 207 | mlarray.push(cmlarray) 208 | cmlarray = [] 209 | let toclose = 1 210 | 211 | cmlarray.push('DUP') 212 | cmlarray.push('CDR') 213 | cmlarray.push('NIL operation') 214 | cmlarray.push('PAIR') 215 | cmlarray.push('SWAP') 216 | cmlarray.push('CAR') 217 | 218 | if (allowDefault) { 219 | cmlarray.push('DUP') 220 | cmlarray.push('PUSH bytes 0x') 221 | cmlarray.push('COMPARE') 222 | cmlarray.push('EQ') 223 | cmlarray.push(['IF']) 224 | mlarray.push(cmlarray) 225 | cmlarray = [] 226 | 227 | cmlarray.push('DROP') 228 | mlarray[mlarray.length - 1][mlarray[mlarray.length - 1].length - 1].push(cmlarray) 229 | cmlarray = [] 230 | toclose++ 231 | } 232 | cmlarray.push('DUP') 233 | cmlarray.push('PUSH nat 4') 234 | cmlarray.push('PUSH nat 0') 235 | cmlarray.push('SLICE') 236 | cmlarray = cmlarray.concat(core.compile.error(100)) 237 | 238 | for (var i = 0; i < mlcode.length; i++) { 239 | cmlarray.push('DUP') 240 | cmlarray.push(`PUSH bytes ${core.map.entry[i].id}`) 241 | cmlarray.push('COMPARE') 242 | cmlarray.push('EQ') 243 | cmlarray.push(['IF']) 244 | mlarray.push(cmlarray) 245 | cmlarray = [] 246 | 247 | cmlarray.push('DROP') 248 | cmlarray = cmlarray.concat(mlcode[i]) 249 | mlarray[mlarray.length - 1][mlarray[mlarray.length - 1].length - 1].push(cmlarray) 250 | cmlarray = [] 251 | toclose++ 252 | } 253 | cmlarray.push('DROP') 254 | cmlarray.push('PUSH nat 400') 255 | cmlarray.push('FAILWITH') 256 | 257 | for (var i = 0; i < toclose; i++) { 258 | mlarray[mlarray.length - 1][mlarray[mlarray.length - 1].length - 1].push(cmlarray) 259 | cmlarray = mlarray.pop() 260 | } 261 | 262 | abi = core.map 263 | 264 | // Format config 265 | 266 | if (config.optimized_abi == 'compact') { 267 | if (typeof abi.function != 'undefined') { 268 | delete abi.function 269 | } 270 | if (typeof abi.const != 'undefined') { 271 | delete abi.const 272 | } 273 | for (var i = 0; i < abi.entry.length; i++) { 274 | delete abi.entry[i].temp 275 | delete abi.entry[i].code 276 | } 277 | } 278 | if (!config.macros) { 279 | // ml = formatRemoveMacros(ml); 280 | } 281 | if (config.ml_format == 'array') { 282 | ml = cmlarray 283 | } 284 | else if (config.ml_format == 'readable') { 285 | ml = formatMl(cmlarray, 0, true) 286 | } 287 | else { 288 | ml = formatMl(cmlarray, 0) 289 | } 290 | 291 | return { 292 | ml 293 | , abi: JSON.stringify(abi) 294 | , config 295 | } 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /lib/abi/helper.js: -------------------------------------------------------------------------------- 1 | const {TextEncoder} = require('text-encoding') 2 | const BN = require('bignumber.js') 3 | const encodeRawBytes = function(input) { 4 | const rec = function(input) { 5 | const result = [] 6 | 7 | if (input instanceof Array) { 8 | result.push('02') 9 | const bytes = input.map(x => rec(x)).join('') 10 | const len = bytes.length / 2 11 | result.push(len.toString(16).padStart(8, '0')) 12 | result.push(bytes) 13 | } 14 | else if (input instanceof Object) { 15 | if (input.prim) { 16 | const args_len = input.args ? input.args.length : 0 17 | result.push(prim_mapping_reverse[args_len][Boolean(input.annots)]) 18 | result.push(op_mapping_reverse[input.prim]) 19 | if (input.args) { 20 | input.args.forEach(arg => result.push(rec(arg))) 21 | } 22 | 23 | if (input.annots) { 24 | const annots_bytes = input.annots.map(x => utility.buf2hex(new TextEncoder().encode(x))).join('20') 25 | result.push((annots_bytes.length / 2).toString(16).padStart(8, '0')) 26 | result.push(annots_bytes) 27 | } 28 | } 29 | else if (input.bytes) { 30 | const len = input.bytes.length / 2 31 | result.push('0A') 32 | result.push(len.toString(16).padStart(8, '0')) 33 | result.push(input.bytes) 34 | } 35 | else if (input.int) { 36 | const num = new BN(input.int, 10) 37 | const positive_mark = num.toString(2)[0] === '-' ? '1' : '0' 38 | const binary = num.toString(2).replace('-', '') 39 | const pad = binary.length <= 6 ? 6 : ((binary.length - 6) % 7 ? binary.length + 7 - (binary.length - 6) % 7 : binary.length) 40 | 41 | const splitted = binary.padStart(pad, '0').match(/\d{6,7}/g) 42 | const reversed = splitted.reverse() 43 | 44 | reversed[0] = positive_mark + reversed[0] 45 | const num_hex = reversed.map((x, i) => parseInt((i === reversed.length - 1 ? '0' : '1') + x, 2) 46 | .toString(16) 47 | .padStart(2, '0')).join('') 48 | 49 | result.push('00') 50 | result.push(num_hex) 51 | } 52 | else if (input.string) { 53 | const string_bytes = new TextEncoder().encode(input.string) 54 | const string_hex = [].slice.call(string_bytes).map(x => x.toString(16).padStart(2, '0')) 55 | .join('') 56 | const len = string_bytes.length 57 | result.push('01') 58 | result.push(len.toString(16).padStart(8, '0')) 59 | result.push(string_hex) 60 | } 61 | } 62 | return result.join('') 63 | } 64 | 65 | return `05${rec(input).toLowerCase()}` 66 | } 67 | const sexp2mic = function me(mi) { 68 | mi = mi.replace(/(?:@[a-z_]+)|(?:#.*$)/mg, '') 69 | .replace(/\s+/g, ' ') 70 | .trim() 71 | if (mi.charAt(0) === '(') { 72 | mi = mi.slice(1, -1) 73 | } 74 | let pl = 0 75 | let sopen = false 76 | let escaped = false 77 | const ret = { 78 | prim: '' 79 | , args: [] 80 | } 81 | let val = '' 82 | for (let i = 0; i < mi.length; i++) { 83 | if (escaped) { 84 | val += mi[i] 85 | escaped = false 86 | continue 87 | } 88 | else if ((i === (mi.length - 1) && sopen === false) || (mi[i] === ' ' && pl === 0 && sopen === false)) { 89 | if (i === (mi.length - 1)) { 90 | val += mi[i] 91 | } 92 | if (val) { 93 | if (val === parseInt(val).toString()) { 94 | if (!ret.prim) { 95 | return {int: val} 96 | } 97 | ret.args.push({int: val}) 98 | } 99 | else if (val[0] == '0') { 100 | if (!ret.prim) { 101 | return {bytes: val} 102 | } 103 | ret.args.push({bytes: val}) 104 | } 105 | else if (ret.prim) { 106 | ret.args.push(me(val)) 107 | } 108 | else { 109 | ret.prim = val 110 | } 111 | val = '' 112 | } 113 | continue 114 | } 115 | else if (mi[i] === '"' && sopen) { 116 | sopen = false 117 | if (!ret.prim) { 118 | return {string: val} 119 | } 120 | ret.args.push({string: val}) 121 | val = '' 122 | continue 123 | } 124 | else if (mi[i] === '"' && !sopen && pl === 0) { 125 | sopen = true 126 | continue 127 | } 128 | else if (mi[i] === '\\') { 129 | escaped = true 130 | } 131 | else if (mi[i] === '(') { 132 | pl++ 133 | } 134 | else if (mi[i] === ')') { 135 | pl-- 136 | } 137 | val += mi[i] 138 | } 139 | return ret 140 | } 141 | const op_mapping = { 142 | '00': 'parameter' 143 | , '01': 'storage' 144 | , '02': 'code' 145 | , '03': 'False' 146 | , '04': 'Elt' 147 | , '05': 'Left' 148 | , '06': 'None' 149 | , '07': 'Pair' 150 | , '08': 'Right' 151 | , '09': 'Some' 152 | , '0A': 'True' 153 | , '0B': 'Unit' 154 | , '0C': 'PACK' 155 | , '0D': 'UNPACK' 156 | , '0E': 'BLAKE2B' 157 | , '0F': 'SHA256' 158 | , 10: 'SHA512' 159 | , 11: 'ABS' 160 | , 12: 'ADD' 161 | , 13: 'AMOUNT' 162 | , 14: 'AND' 163 | , 15: 'BALANCE' 164 | , 16: 'CAR' 165 | , 17: 'CDR' 166 | , 18: 'CHECK_SIGNATURE' 167 | , 19: 'COMPARE' 168 | , '1A': 'CONCAT' 169 | , '1B': 'CONS' 170 | , '1C': 'CREATE_ACCOUNT' 171 | , '1D': 'CREATE_CONTRACT' 172 | , '1E': 'IMPLICIT_ACCOUNT' 173 | , '1F': 'DIP' 174 | , 20: 'DROP' 175 | , 21: 'DUP' 176 | , 22: 'EDIV' 177 | , 23: 'EMPTY_MAP' 178 | , 24: 'EMPTY_SET' 179 | , 25: 'EQ' 180 | , 26: 'EXEC' 181 | , 27: 'FAILWITH' 182 | , 28: 'GE' 183 | , 29: 'GET' 184 | , '2A': 'GT' 185 | , '2B': 'HASH_KEY' 186 | , '2C': 'IF' 187 | , '2D': 'IF_CONS' 188 | , '2E': 'IF_LEFT' 189 | , '2F': 'IF_NONE' 190 | , 30: 'INT' 191 | , 31: 'LAMBDA' 192 | , 32: 'LE' 193 | , 33: 'LEFT' 194 | , 34: 'LOOP' 195 | , 35: 'LSL' 196 | , 36: 'LSR' 197 | , 37: 'LT' 198 | , 38: 'MAP' 199 | , 39: 'MEM' 200 | , '3A': 'MUL' 201 | , '3B': 'NEG' 202 | , '3C': 'NEQ' 203 | , '3D': 'NIL' 204 | , '3E': 'NONE' 205 | , '3F': 'NOT' 206 | , 40: 'NOW' 207 | , 41: 'OR' 208 | , 42: 'PAIR' 209 | , 43: 'PUSH' 210 | , 44: 'RIGHT' 211 | , 45: 'SIZE' 212 | , 46: 'SOME' 213 | , 47: 'SOURCE' 214 | , 48: 'SENDER' 215 | , 49: 'SELF' 216 | , '4A': 'STEPS_TO_QUOTA' 217 | , '4B': 'SUB' 218 | , '4C': 'SWAP' 219 | , '4D': 'TRANSFER_TOKENS' 220 | , '4E': 'SET_DELEGATE' 221 | , '4F': 'UNIT' 222 | , 50: 'UPDATE' 223 | , 51: 'XOR' 224 | , 52: 'ITER' 225 | , 53: 'LOOP_LEFT' 226 | , 54: 'ADDRESS' 227 | , 55: 'CONTRACT' 228 | , 56: 'ISNAT' 229 | , 57: 'CAST' 230 | , 58: 'RENAME' 231 | , 59: 'bool' 232 | , '5A': 'contract' 233 | , '5B': 'int' 234 | , '5C': 'key' 235 | , '5D': 'key_hash' 236 | , '5E': 'lambda' 237 | , '5F': 'list' 238 | , 60: 'map' 239 | , 61: 'big_map' 240 | , 62: 'nat' 241 | , 63: 'option' 242 | , 64: 'or' 243 | , 65: 'pair' 244 | , 66: 'set' 245 | , 67: 'signature' 246 | , 68: 'string' 247 | , 69: 'bytes' 248 | , '6A': 'mutez' 249 | , '6B': 'timestamp' 250 | , '6C': 'unit' 251 | , '6D': 'operation' 252 | , '6E': 'address' 253 | , '6F': 'SLICE' 254 | } 255 | var op_mapping_reverse = (function() { 256 | const result = {} 257 | for (const key in op_mapping) { 258 | result[op_mapping[key]] = key 259 | } 260 | return result 261 | }()) 262 | const prim_mapping = { 263 | '00': 'int' 264 | , '01': 'string' 265 | , '02': 'seq' 266 | , '03': { 267 | name: 'prim' 268 | , len: 0 269 | , annots: false 270 | } 271 | , '04': { 272 | name: 'prim' 273 | , len: 0 274 | , annots: true 275 | } 276 | , '05': { 277 | name: 'prim' 278 | , len: 1 279 | , annots: false 280 | } 281 | , '06': { 282 | name: 'prim' 283 | , len: 1 284 | , annots: true 285 | } 286 | , '07': { 287 | name: 'prim' 288 | , len: 2 289 | , annots: false 290 | } 291 | , '08': { 292 | name: 'prim' 293 | , len: 2 294 | , annots: true 295 | } 296 | , '09': { 297 | name: 'prim' 298 | , len: 3 299 | , annots: true 300 | } 301 | , '0A': 'bytes' 302 | } 303 | var prim_mapping_reverse = { 304 | 0: { 305 | false: '03' 306 | , true: '04' 307 | } 308 | , 1: { 309 | false: '05' 310 | , true: '06' 311 | } 312 | , 2: { 313 | false: '07' 314 | , true: '08' 315 | } 316 | , 3: { 317 | true: '09' 318 | } 319 | } 320 | 321 | module.exports = function(core) { 322 | var typevalue = function(p, ts, abi) { 323 | var p = p.slice(0) 324 | var ts = ts.slice(0) 325 | if (typeof p == 'string') { 326 | p = [[p]] 327 | } 328 | if (typeof p[0] == 'string') { 329 | p = [p] 330 | } 331 | if (p.length == 0) { 332 | return 'Unit' 333 | } 334 | else if (p.length == 1) { 335 | if (core.literalTypes.indexOf(p[0][0]) >= 0) { 336 | return core.compile.literal(p[0][0], ts[0]) 337 | } 338 | else if (p[0][0] == 'unit') { 339 | return 'Unit' 340 | } 341 | else if (core.complexTypes.indexOf(p[0][0]) >= 0) { 342 | return core.compile.literal(p[0][0], ts[0]) 343 | } 344 | 345 | return namedTypevalue(abi.struct[core.helper.findInObjArray(abi.struct, 'name', p[0][0])].type, ts[0], abi) 346 | } 347 | 348 | let ret = '(Pair ' 349 | ret += typevalue([p.shift()], [ts.shift()], abi) 350 | ret += ' ' 351 | let e = '' 352 | while (p.length > 1) { 353 | ret += '(Pair ' 354 | ret += typevalue([p.shift()], [ts.shift()], abi) 355 | ret += ' ' 356 | e += ')' 357 | } 358 | ret += typevalue([p.shift()], [ts.shift()], abi) 359 | ret += ')' 360 | ret += e 361 | return ret 362 | } 363 | var namedTypevalue = function(p, ts, abi) { 364 | var p = p.slice(0) 365 | if (p.length == 0) { 366 | return 'Unit' 367 | } 368 | const types = [] 369 | const vals = [] 370 | while (p.length) { 371 | const tt = p.shift() 372 | types.push(tt.type) 373 | if (typeof ts[tt.name] == 'undefined') { 374 | throw `Error with JSON ${tt.name}` 375 | } 376 | vals.push(ts[tt.name]) 377 | } 378 | return typevalue(types, vals, abi) 379 | } 380 | return { 381 | packData(d) { 382 | return encodeRawBytes(sexp2mic(d)) 383 | } 384 | , namedTypevalue 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /e2e/__snapshots__/test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Compiler bigmap sample compiles correctly 1`] = ` 4 | Object { 5 | "abi": "{\\"storage\\":[{\\"name\\":\\"bmaptest\\",\\"type\\":[\\"big_map\\",[\\"nat\\"],[\\"address\\"]]}],\\"entry\\":[{\\"name\\":\\"TestBigmap\\",\\"id\\":\\"0x7185ce72\\",\\"input\\":[],\\"temp\\":[{\\"name\\":\\"test2\\",\\"type\\":[\\"address\\"]}],\\"code\\":[[\\"ext\\",\\"push\\",\\"storage.bmaptest\\",[\\"nat\\",\\"1\\"],[\\"SENDER\\"]],[\\"ext\\",\\"push\\",\\"storage.bmaptest\\",[\\"nat\\",\\"2\\"],[\\"address\\",\\"tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM\\"]],[\\"set\\",\\"test2\\",[\\"ext\\",\\"get\\",\\"storage.bmaptest\\",[\\"nat\\",\\"1\\"]]],[\\"if\\",[\\"eq\\",[\\"ext\\",\\"in\\",\\"storage.bmaptest\\",[\\"nat\\",\\"1\\"]]],[\\"ext\\",\\"drop\\",\\"storage.bmaptest\\",[\\"nat\\",\\"2\\"]]]]}]}", 6 | "config": Object { 7 | "abi_format": "compact", 8 | "macros": true, 9 | "ml_format": "compact", 10 | }, 11 | "ml": "parameter bytes;storage (big_map nat address);code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0x7185ce72;COMPARE;EQ;IF{DROP;DROP;NONE address;PAIR;PUSH nat 1;DIP{SENDER;SOME};DIIP{DUP;CDDR};UPDATE;SWAP;SET_CDDR;PUSH nat 2;DIP{PUSH address \\"tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM\\";SOME};DIIP{DUP;CDDR};UPDATE;SWAP;SET_CDDR;PUSH nat 1;DIP{DUP;CDDR};GET;IF_NONE{PUSH string \\"Key not found in map\\";FAILWITH}{};SWAP;SET_CAR;PUSH nat 1;DIP{DUP;CDDR};MEM;DIP{PUSH bool True};COMPARE;EQ;IF{PUSH nat 2;DIP{NONE address};DIIP{DUP;CDDR};UPDATE;SWAP;SET_CDDR}{};CDR}{DROP;PUSH nat 400;FAILWITH}}", 12 | } 13 | `; 14 | 15 | exports[`Compiler blank sample compiles correctly 1`] = ` 16 | Object { 17 | "abi": "{\\"entry\\":[{\\"name\\":\\"TestBlank\\",\\"id\\":\\"0xff053d71\\",\\"input\\":[],\\"temp\\":[],\\"code\\":[]}]}", 18 | "config": Object { 19 | "abi_format": "compact", 20 | "macros": true, 21 | "ml_format": "compact", 22 | }, 23 | "ml": "parameter bytes;storage unit;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0xff053d71;COMPARE;EQ;IF{DROP;DROP}{DROP;PUSH nat 400;FAILWITH}}", 24 | } 25 | `; 26 | 27 | exports[`Compiler bool sample compiles correctly 1`] = ` 28 | Object { 29 | "abi": "{\\"entry\\":[{\\"name\\":\\"TestBool\\",\\"id\\":\\"0x03869439\\",\\"input\\":[],\\"temp\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"bool\\"]}],\\"code\\":[[\\"set\\",\\"test1\\",[\\"bool\\",\\"True\\"]],[\\"if\\",[\\"eq\\",[\\"test1\\"],[\\"bool\\",\\"False\\"]],[[\\"assert\\",[\\"eq\\",[\\"bool\\",\\"True\\"]]],[\\"assert\\",[\\"eq\\",[\\"test1\\"]]]]]]}]}", 30 | "config": Object { 31 | "abi_format": "compact", 32 | "macros": true, 33 | "ml_format": "compact", 34 | }, 35 | "ml": "parameter bytes;storage unit;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0x03869439;COMPARE;EQ;IF{DROP;DROP;NONE bool;PAIR;PUSH bool True;SWAP;SET_CAR;DUP;CAR;DIP{PUSH bool False};COMPARE;EQ;IF{PUSH bool True;DIP{PUSH bool True};COMPARE;EQ;IF{}{PUSH string \\"Failed assert\\";FAILWITH};DUP;CAR;DIP{PUSH bool True};COMPARE;EQ;IF{}{PUSH string \\"Failed assert\\";FAILWITH}}{};CDR}{DROP;PUSH nat 400;FAILWITH}}", 36 | } 37 | `; 38 | 39 | exports[`Compiler bytes sample compiles correctly 1`] = ` 40 | Object { 41 | "abi": "{\\"entry\\":[{\\"name\\":\\"TestBytes\\",\\"id\\":\\"0xa204dd7c\\",\\"input\\":[],\\"temp\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"bytes\\"]},{\\"name\\":\\"test2\\",\\"type\\":[\\"bytes\\"]},{\\"name\\":\\"test3\\",\\"type\\":[\\"bytes\\"]},{\\"name\\":\\"test4\\",\\"type\\":[\\"bytes\\"]},{\\"name\\":\\"test5\\",\\"type\\":[\\"bytes\\"]},{\\"name\\":\\"test6\\",\\"type\\":[\\"bytes\\"]},{\\"name\\":\\"test7\\",\\"type\\":[\\"nat\\"]},{\\"name\\":\\"test8\\",\\"type\\":[\\"nat\\"]},{\\"name\\":\\"test9\\",\\"type\\":[\\"bytes\\"]}],\\"code\\":[[\\"set\\",\\"test1\\",[\\"bytes\\",\\"0x0500\\"]],[\\"set\\",\\"test1\\",[\\"concat\\",[\\"test1\\"],[\\"bytes\\",\\"0x050001\\"]]],[\\"set\\",\\"test2\\",[\\"hash\\",[\\"bytes\\",\\"0x050001\\"]]],[\\"set\\",\\"test3\\",[\\"hash\\",[\\"bytes\\",\\"0x050001\\"],[\\"blake2b\\"]]],[\\"set\\",\\"test4\\",[\\"hash\\",[\\"bytes\\",\\"0x050001\\"],[\\"sha256\\"]]],[\\"set\\",\\"test5\\",[\\"hash\\",[\\"bytes\\",\\"0x050001\\"],[\\"sha512\\"]]],[\\"set\\",\\"test6\\",[\\"pack\\",[\\"nat\\",\\"1\\"]]],[\\"set\\",\\"test7\\",[\\"unpack\\",[\\"test3\\"],[\\"nat\\"]]],[\\"set\\",\\"test8\\",[\\"ext\\",\\"length\\",\\"test6\\"]],[\\"set\\",\\"test9\\",[\\"slice\\",[\\"test2\\"],[\\"nat\\",\\"0\\"],[\\"nat\\",\\"4\\"]]]]}]}", 42 | "config": Object { 43 | "abi_format": "compact", 44 | "macros": true, 45 | "ml_format": "compact", 46 | }, 47 | "ml": "parameter bytes;storage unit;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0xa204dd7c;COMPARE;EQ;IF{DROP;DROP;NONE bytes;NONE nat;PAIR;NONE nat;PAIR;NONE bytes;PAIR;NONE bytes;PAIR;NONE bytes;PAIR;NONE bytes;PAIR;NONE bytes;PAIR;NONE bytes;PAIR;PAIR;PUSH bytes 0x0500;SWAP;SET_CAAR;DUP;CAAR;DIP{PUSH bytes 0x050001};CONCAT;SWAP;SET_CAAR;PUSH bytes 0x050001;BLAKE2B;SWAP;SET_CADAR;PUSH bytes 0x050001;BLAKE2B;SWAP;SET_CADDAR;PUSH bytes 0x050001;SHA256;SWAP;SET_CADDDAR;PUSH bytes 0x050001;SHA512;SWAP;SET_CADDDDAR;PUSH nat 1;PACK;SWAP;SET_CADDDDDAR;DUP;CADDAR;UNPACK nat;IF_NONE{PUSH string \\"Unable to unpack\\";FAILWITH}{};SWAP;SET_CADDDDDDAR;DUP;CADDDDDAR;SIZE;SWAP;SET_CADDDDDDDAR;PUSH nat 0;DIP{PUSH nat 4};DIIP{DUP;CADAR};SLICE;IF_NONE{PUSH string \\"Unable to slice\\";FAILWITH}{};SWAP;SET_CADDDDDDDDR;CDR}{DROP;PUSH nat 400;FAILWITH}}", 48 | } 49 | `; 50 | 51 | exports[`Compiler input sample compiles correctly 1`] = ` 52 | Object { 53 | "abi": "{\\"storage\\":[{\\"name\\":\\"test\\",\\"type\\":[\\"nat\\"]}],\\"entry\\":[{\\"name\\":\\"TestInput\\",\\"id\\":\\"0x9e642eca\\",\\"input\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"nat\\"]}],\\"temp\\":[],\\"code\\":[[\\"set\\",\\"storage.test\\",[\\"input.test1\\"]]]}]}", 54 | "config": Object { 55 | "abi_format": "compact", 56 | "macros": true, 57 | "ml_format": "compact", 58 | }, 59 | "ml": "parameter bytes;storage nat;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0x9e642eca;COMPARE;EQ;IF{DROP;DUP;SIZE;PUSH nat 4;SWAP;SUB;DUP;GT;IF{}{PUSH nat 102;FAILWITH};ABS;PUSH nat 4;SLICE;IF_NONE{PUSH nat 101;FAILWITH}{};UNPACK nat;IF_NONE{PUSH nat 103;FAILWITH}{};PAIR;DUP;CAR;SWAP;SET_CDDR;CDR}{DROP;PUSH nat 400;FAILWITH}}", 60 | } 61 | `; 62 | 63 | exports[`Compiler key sample compiles correctly 1`] = ` 64 | Object { 65 | "abi": "{\\"entry\\":[{\\"name\\":\\"TestKey\\",\\"id\\":\\"0x1be907f4\\",\\"input\\":[],\\"temp\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"key\\"]},{\\"name\\":\\"test2\\",\\"type\\":[\\"key_hash\\"]},{\\"name\\":\\"test3\\",\\"type\\":[\\"address\\"]},{\\"name\\":\\"test4\\",\\"type\\":[\\"contract\\",[\\"unit\\"]]},{\\"name\\":\\"test5\\",\\"type\\":[\\"address\\"]},{\\"name\\":\\"test6\\",\\"type\\":[\\"bool\\"]}],\\"code\\":[[\\"set\\",\\"test1\\",[\\"key\\",\\"edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym\\"]],[\\"set\\",\\"test2\\",[\\"to_pkh\\",[\\"key\\",\\"edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym\\"]]],[\\"set\\",\\"test3\\",[\\"to_address\\",[\\"key\\",\\"edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym\\"]]],[\\"set\\",\\"test4\\",[\\"to_contract\\",[\\"test1\\"]]],[\\"set\\",\\"test5\\",[\\"to_address\\",[\\"test4\\"]]],[\\"transfer\\",[\\"test1\\"],[\\"mutez\\",\\"0\\"]],[\\"transfer\\",[\\"test2\\"],[\\"mutez\\",\\"0\\"]],[\\"transfer\\",[\\"test3\\"],[\\"mutez\\",\\"0\\"]],[\\"transfer\\",[\\"test4\\"],[\\"mutez\\",\\"0\\"]],[\\"transfer\\",[\\"test5\\"],[\\"mutez\\",\\"0\\"]],[\\"set\\",\\"test6\\",[\\"verify\\",[\\"bytes\\",\\"0x0500\\"],[\\"signature\\",\\"edsigtgjPezMVW9zCu8ny4uMaAsT3gqbRANSNAzVBvZoFXx3ZiPifbLUUthdj4tewcCvJf3HpPfMTnML73hCi6UnFvZpvWsqTBk\\"],[\\"test1\\"]]]]}]}", 66 | "config": Object { 67 | "abi_format": "compact", 68 | "macros": true, 69 | "ml_format": "compact", 70 | }, 71 | "ml": "parameter bytes;storage unit;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0x1be907f4;COMPARE;EQ;IF{DROP;DROP;NONE bool;NONE address;PAIR;NONE (contract unit);PAIR;NONE address;PAIR;NONE key_hash;PAIR;NONE key;PAIR;PAIR;PUSH key \\"edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym\\";SWAP;SET_CAAR;PUSH key \\"edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym\\";HASH_KEY;SWAP;SET_CADAR;PUSH key \\"edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym\\";HASH_KEY;IMPLICIT_ACCOUNT;ADDRESS;SWAP;SET_CADDAR;DUP;CAAR;HASH_KEY;IMPLICIT_ACCOUNT;SWAP;SET_CADDDAR;DUP;CADDDAR;ADDRESS;SWAP;SET_CADDDDAR;UNIT;DIP{PUSH mutez 0};DIIP{DUP;CAAR;HASH_KEY;IMPLICIT_ACCOUNT};TRANSFER_TOKENS;DIP{DUP;CDAR};CONS;SWAP;SET_CDAR;UNIT;DIP{PUSH mutez 0};DIIP{DUP;CADAR;IMPLICIT_ACCOUNT};TRANSFER_TOKENS;DIP{DUP;CDAR};CONS;SWAP;SET_CDAR;UNIT;DIP{PUSH mutez 0};DIIP{DUP;CADDAR;CONTRACT unit;IF_NONE{PUSH string \\"Invalid contract\\";FAILWITH}{}};TRANSFER_TOKENS;DIP{DUP;CDAR};CONS;SWAP;SET_CDAR;UNIT;DIP{PUSH mutez 0};DIIP{DUP;CADDDAR};TRANSFER_TOKENS;DIP{DUP;CDAR};CONS;SWAP;SET_CDAR;UNIT;DIP{PUSH mutez 0};DIIP{DUP;CADDDDAR;CONTRACT unit;IF_NONE{PUSH string \\"Invalid contract\\";FAILWITH}{}};TRANSFER_TOKENS;DIP{DUP;CDAR};CONS;SWAP;SET_CDAR;DUP;CAAR;DIP{PUSH signature \\"edsigtgjPezMVW9zCu8ny4uMaAsT3gqbRANSNAzVBvZoFXx3ZiPifbLUUthdj4tewcCvJf3HpPfMTnML73hCi6UnFvZpvWsqTBk\\"};DIIP{PUSH bytes 0x0500};CHECK_SIGNATURE;SWAP;SET_CADDDDDR;CDR}{DROP;PUSH nat 400;FAILWITH}}", 72 | } 73 | `; 74 | 75 | exports[`Compiler key_hash sample compiles correctly 1`] = ` 76 | Object { 77 | "abi": "{\\"entry\\":[{\\"name\\":\\"TestPkh\\",\\"id\\":\\"0xa8ecbe7f\\",\\"input\\":[],\\"temp\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"key_hash\\"]},{\\"name\\":\\"test2\\",\\"type\\":[\\"address\\"]},{\\"name\\":\\"test3\\",\\"type\\":[\\"contract\\",[\\"unit\\"]]}],\\"code\\":[[\\"set\\",\\"test1\\",[\\"pkh\\",\\"tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM\\"]],[\\"set\\",\\"test2\\",[\\"to_address\\",[\\"pkh\\",\\"tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM\\"]]],[\\"set\\",\\"test3\\",[\\"to_contract\\",[\\"test1\\"]]],[\\"transfer\\",[\\"test1\\"],[\\"mutez\\",\\"0\\"]],[\\"transfer\\",[\\"test2\\"],[\\"mutez\\",\\"0\\"]],[\\"transfer\\",[\\"test3\\"],[\\"mutez\\",\\"0\\"]],[\\"delegate\\"],[\\"delegate\\",[\\"test1\\"]],[\\"delegate\\",[\\"pkh\\",\\"tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM\\"]]]}]}", 78 | "config": Object { 79 | "abi_format": "compact", 80 | "macros": true, 81 | "ml_format": "compact", 82 | }, 83 | "ml": "parameter bytes;storage unit;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0xa8ecbe7f;COMPARE;EQ;IF{DROP;DROP;NONE (contract unit);NONE address;PAIR;NONE key_hash;PAIR;PAIR;PUSH key_hash \\"tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM\\";SWAP;SET_CAAR;PUSH key_hash \\"tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM\\";IMPLICIT_ACCOUNT;ADDRESS;SWAP;SET_CADAR;DUP;CAAR;IMPLICIT_ACCOUNT;SWAP;SET_CADDR;UNIT;DIP{PUSH mutez 0};DIIP{DUP;CAAR;IMPLICIT_ACCOUNT};TRANSFER_TOKENS;DIP{DUP;CDAR};CONS;SWAP;SET_CDAR;UNIT;DIP{PUSH mutez 0};DIIP{DUP;CADAR;CONTRACT unit;IF_NONE{PUSH string \\"Invalid contract\\";FAILWITH}{}};TRANSFER_TOKENS;DIP{DUP;CDAR};CONS;SWAP;SET_CDAR;UNIT;DIP{PUSH mutez 0};DIIP{DUP;CADDR};TRANSFER_TOKENS;DIP{DUP;CDAR};CONS;SWAP;SET_CDAR;NONE key_hash;SET_DELEGATE;DIP{DUP;CDAR};CONS;SWAP;SET_CDAR;DUP;CAAR;SOME;SET_DELEGATE;DIP{DUP;CDAR};CONS;SWAP;SET_CDAR;PUSH key_hash \\"tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM\\";SOME;SET_DELEGATE;DIP{DUP;CDAR};CONS;SWAP;SET_CDAR;CDR}{DROP;PUSH nat 400;FAILWITH}}", 84 | } 85 | `; 86 | 87 | exports[`Compiler list sample compiles correctly 1`] = ` 88 | Object { 89 | "abi": "{\\"entry\\":[{\\"name\\":\\"TestList\\",\\"id\\":\\"0x27e5d125\\",\\"input\\":[],\\"temp\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"list\\",[\\"address\\"]]},{\\"name\\":\\"test2\\",\\"type\\":[\\"address\\"]},{\\"name\\":\\"test3\\",\\"type\\":[\\"nat\\"]}],\\"code\\":[[\\"set\\",\\"test1\\",[\\"new\\",\\"list\\",[\\"address\\"]]],[\\"ext\\",\\"push\\",\\"test1\\",[\\"SENDER\\"]],[\\"set\\",\\"test2\\",[\\"ext\\",\\"pop\\",\\"test1\\"]],[\\"set\\",\\"test3\\",[\\"ext\\",\\"length\\",\\"test1\\"]]]}]}", 90 | "config": Object { 91 | "abi_format": "compact", 92 | "macros": true, 93 | "ml_format": "compact", 94 | }, 95 | "ml": "parameter bytes;storage unit;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0x27e5d125;COMPARE;EQ;IF{DROP;DROP;NONE nat;NONE address;PAIR;NONE (list address);PAIR;PAIR;NIL address;SWAP;SET_CAAR;SENDER;DIP{DUP;CAAR};CONS;SWAP;SET_CAAR;DUP;CAAR;IF_CONS{}{PUSH string \\"Fail pop\\";FAILWITH};DIP{SWAP;SET_CAAR};SWAP;SET_CADAR;DUP;CAAR;SIZE;SWAP;SET_CADDR;CDR}{DROP;PUSH nat 400;FAILWITH}}", 96 | } 97 | `; 98 | 99 | exports[`Compiler map sample compiles correctly 1`] = ` 100 | Object { 101 | "abi": "{\\"entry\\":[{\\"name\\":\\"TestMap\\",\\"id\\":\\"0x679e0fcd\\",\\"input\\":[],\\"temp\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"map\\",[\\"nat\\"],[\\"address\\"]]},{\\"name\\":\\"test2\\",\\"type\\":[\\"address\\"]},{\\"name\\":\\"test3\\",\\"type\\":[\\"nat\\"]}],\\"code\\":[[\\"set\\",\\"test1\\",[\\"new\\",\\"map\\",[\\"nat\\"],[\\"address\\"]]],[\\"ext\\",\\"push\\",\\"test1\\",[\\"nat\\",\\"1\\"],[\\"SENDER\\"]],[\\"ext\\",\\"push\\",\\"test1\\",[\\"nat\\",\\"2\\"],[\\"address\\",\\"tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM\\"]],[\\"set\\",\\"test2\\",[\\"ext\\",\\"get\\",\\"test1\\",[\\"nat\\",\\"1\\"]]],[\\"if\\",[\\"eq\\",[\\"ext\\",\\"in\\",\\"test1\\",[\\"nat\\",\\"1\\"]]],[\\"ext\\",\\"drop\\",\\"test1\\",[\\"nat\\",\\"2\\"]]],[\\"set\\",\\"test3\\",[\\"ext\\",\\"length\\",\\"test1\\"]]]}]}", 102 | "config": Object { 103 | "abi_format": "compact", 104 | "macros": true, 105 | "ml_format": "compact", 106 | }, 107 | "ml": "parameter bytes;storage unit;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0x679e0fcd;COMPARE;EQ;IF{DROP;DROP;NONE nat;NONE address;PAIR;NONE (map nat address);PAIR;PAIR;EMPTY_MAP nat address;SWAP;SET_CAAR;PUSH nat 1;DIP{SENDER;SOME};DIIP{DUP;CAAR};UPDATE;SWAP;SET_CAAR;PUSH nat 2;DIP{PUSH address \\"tz1UqBubMBKtEVpN6jkNQwc3TAoBNdtmtiyM\\";SOME};DIIP{DUP;CAAR};UPDATE;SWAP;SET_CAAR;PUSH nat 1;DIP{DUP;CAAR};GET;IF_NONE{PUSH string \\"Key not found in map\\";FAILWITH}{};SWAP;SET_CADAR;PUSH nat 1;DIP{DUP;CAAR};MEM;DIP{PUSH bool True};COMPARE;EQ;IF{PUSH nat 2;DIP{NONE address};DIIP{DUP;CAAR};UPDATE;SWAP;SET_CAAR}{};DUP;CAAR;SIZE;SWAP;SET_CADDR;CDR}{DROP;PUSH nat 400;FAILWITH}}", 108 | } 109 | `; 110 | 111 | exports[`Compiler mutez sample compiles correctly 1`] = ` 112 | Object { 113 | "abi": "{\\"entry\\":[{\\"name\\":\\"TestMutez\\",\\"id\\":\\"0xef250cb4\\",\\"input\\":[],\\"temp\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"mutez\\"]},{\\"name\\":\\"test2\\",\\"type\\":[\\"mutez\\"]}],\\"code\\":[[\\"set\\",\\"test1\\",[\\"AMOUNT\\"]],[\\"set\\",\\"test2\\",[\\"add\\",[\\"test1\\"],[\\"AMOUNT\\"],[\\"BALANCE\\"]]]]}]}", 114 | "config": Object { 115 | "abi_format": "compact", 116 | "macros": true, 117 | "ml_format": "compact", 118 | }, 119 | "ml": "parameter bytes;storage unit;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0xef250cb4;COMPARE;EQ;IF{DROP;DROP;NONE mutez;NONE mutez;PAIR;PAIR;AMOUNT;SWAP;SET_CAAR;DUP;CAAR;DIP{AMOUNT};ADD;DIP{BALANCE};ADD;SWAP;SET_CADR;CDR}{DROP;PUSH nat 400;FAILWITH}}", 120 | } 121 | `; 122 | 123 | exports[`Compiler numbers sample compiles correctly 1`] = ` 124 | Object { 125 | "abi": "{\\"entry\\":[{\\"name\\":\\"TestNumbers\\",\\"id\\":\\"0x53bb191c\\",\\"input\\":[],\\"temp\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"nat\\"]},{\\"name\\":\\"test2\\",\\"type\\":[\\"nat\\"]},{\\"name\\":\\"test3\\",\\"type\\":[\\"nat\\"]},{\\"name\\":\\"test4\\",\\"type\\":[\\"int\\"]},{\\"name\\":\\"test5\\",\\"type\\":[\\"mutez\\"]},{\\"name\\":\\"test6\\",\\"type\\":[\\"nat\\"]},{\\"name\\":\\"test7\\",\\"type\\":[\\"nat\\"]},{\\"name\\":\\"test8\\",\\"type\\":[\\"nat\\"]},{\\"name\\":\\"test9\\",\\"type\\":[\\"nat\\"]},{\\"name\\":\\"test10\\",\\"type\\":[\\"nat\\"]},{\\"name\\":\\"test11\\",\\"type\\":[\\"nat\\"]}],\\"code\\":[[\\"set\\",\\"test1\\",[\\"nat\\",\\"100\\"]],[\\"set\\",\\"test2\\",[\\"add\\",[\\"test1\\"],[\\"nat\\",\\"2\\"],[\\"nat\\",\\"3\\"]]],[\\"set\\",\\"test3\\",[\\"to_nat\\",[\\"sub\\",[\\"add\\",[\\"to_int\\",[\\"mutez\\",\\"0\\"]],[\\"int\\",\\"2\\"]],[\\"int\\",\\"3\\"],[\\"nat\\",\\"5\\"]]]],[\\"set\\",\\"test4\\",[\\"to_int\\",[\\"mutez\\",\\"0\\"]]],[\\"set\\",\\"test5\\",[\\"to_mutez\\",[\\"sub\\",[\\"add\\",[\\"to_int\\",[\\"mutez\\",\\"0\\"]],[\\"int\\",\\"2\\"]],[\\"int\\",\\"3\\"],[\\"nat\\",\\"5\\"]]]],[\\"set\\",\\"test6\\",[\\"abs\\",[\\"to_int\\",[\\"test2\\"]]]],[\\"set\\",\\"test7\\",[\\"sqr\\",[\\"test6\\"]]],[\\"set\\",\\"test8\\",[\\"abs\\",[\\"to_int\\",[\\"test2\\"]]]],[\\"set\\",\\"test9\\",[\\"abs\\",[\\"to_int\\",[\\"test2\\"]]]],[\\"set\\",\\"test10\\",[\\"mod\\",[\\"nat\\",\\"10\\"],[\\"nat\\",\\"3\\"]]],[\\"set\\",\\"test11\\",[\\"div\\",[\\"nat\\",\\"10\\"],[\\"nat\\",\\"3\\"]]]]}]}", 126 | "config": Object { 127 | "abi_format": "compact", 128 | "macros": true, 129 | "ml_format": "compact", 130 | }, 131 | "ml": "parameter bytes;storage unit;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0x53bb191c;COMPARE;EQ;IF{DROP;DROP;NONE nat;NONE nat;PAIR;NONE nat;PAIR;NONE nat;PAIR;NONE nat;PAIR;NONE nat;PAIR;NONE mutez;PAIR;NONE int;PAIR;NONE nat;PAIR;NONE nat;PAIR;NONE nat;PAIR;PAIR;PUSH nat 100;SWAP;SET_CAAR;DUP;CAAR;DIP{PUSH nat 2};ADD;DIP{PUSH nat 3};ADD;SWAP;SET_CADAR;PUSH mutez 0;DIP{PUSH mutez 1};EDIV;IF_NONE{PUSH string \\"Error with casting\\";FAILWITH}{};CAR;PUSH int 1;MUL;DIP{PUSH int 2};ADD;DIP{PUSH int 3};SUB;DIP{PUSH nat 5};SUB;DUP;GT;IF{}{PUSH string \\"Nat conversion not possible\\";FAILWITH};ABS;SWAP;SET_CADDAR;PUSH mutez 0;DIP{PUSH mutez 1};EDIV;IF_NONE{PUSH string \\"Error with casting\\";FAILWITH}{};CAR;PUSH int 1;MUL;SWAP;SET_CADDDAR;PUSH mutez 0;DIP{PUSH mutez 1};EDIV;IF_NONE{PUSH string \\"Error with casting\\";FAILWITH}{};CAR;PUSH int 1;MUL;DIP{PUSH int 2};ADD;DIP{PUSH int 3};SUB;DIP{PUSH nat 5};SUB;DUP;GT;IF{}{PUSH string \\"Mutez conversion not possible\\";FAILWITH};ABS;DIP{PUSH mutez 1};MUL;SWAP;SET_CADDDDAR;DUP;CADAR;PUSH int 1;MUL;ABS;SWAP;SET_CADDDDDAR;DUP;CADDDDDAR;DUP;MUL;SWAP;SET_CADDDDDDAR;DUP;CADAR;PUSH int 1;MUL;ABS;SWAP;SET_CADDDDDDDAR;DUP;CADAR;PUSH int 1;MUL;ABS;SWAP;SET_CADDDDDDDDAR;PUSH nat 10;DIP{PUSH nat 3};EDIV;IF_NONE{PUSH string \\"Divisible by 0\\";FAILWITH}{};CDR;SWAP;SET_CADDDDDDDDDAR;PUSH nat 10;DIP{PUSH nat 3};EDIV;IF_NONE{PUSH string \\"Divisible by 0\\";FAILWITH}{};CAR;SWAP;SET_CADDDDDDDDDDR;CDR}{DROP;PUSH nat 400;FAILWITH}}", 132 | } 133 | `; 134 | 135 | exports[`Compiler optional sample compiles correctly 1`] = ` 136 | Object { 137 | "abi": "{\\"entry\\":[{\\"name\\":\\"TestOptional\\",\\"id\\":\\"0x69847f58\\",\\"input\\":[],\\"temp\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"option\\",[\\"nat\\"]]},{\\"name\\":\\"test2\\",\\"type\\":[\\"bool\\"]},{\\"name\\":[\\"to_some\\",[\\"test1\\"]],\\"type\\":[\\"nat\\"]}],\\"code\\":[[\\"set\\",\\"test1\\",[\\"to_optional\\",[\\"nat\\",\\"1\\"]]],[\\"set\\",\\"test2\\",[\\"isset\\",[\\"test1\\"]]]]}]}", 138 | "config": Object { 139 | "abi_format": "compact", 140 | "macros": true, 141 | "ml_format": "compact", 142 | }, 143 | "ml": "parameter bytes;storage unit;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0x69847f58;COMPARE;EQ;IF{DROP;DROP;NONE nat;NONE bool;PAIR;NONE (option nat);PAIR;PAIR;PUSH nat 1;SOME;SWAP;SET_CAAR;DUP;CAAR;IF_NONE{PUSH bool False}{DROP;PUSH bool True};SWAP;SET_CADAR;CDR}{DROP;PUSH nat 400;FAILWITH}}", 144 | } 145 | `; 146 | 147 | exports[`Compiler set sample compiles correctly 1`] = ` 148 | Object { 149 | "abi": "{\\"entry\\":[{\\"name\\":\\"TestSet\\",\\"id\\":\\"0x36203e28\\",\\"input\\":[],\\"temp\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"set\\",[\\"nat\\"]]},{\\"name\\":\\"test2\\",\\"type\\":[\\"nat\\"]},{\\"name\\":\\"test3\\",\\"type\\":[\\"nat\\"]}],\\"code\\":[[\\"set\\",\\"test1\\",[\\"new\\",\\"set\\",[\\"nat\\"]]],[\\"ext\\",\\"push\\",\\"test1\\",[\\"nat\\",\\"100\\"]],[\\"ext\\",\\"push\\",\\"test1\\",[\\"nat\\",\\"200\\"]],[\\"ext\\",\\"push\\",\\"test1\\",[\\"nat\\",\\"300\\"]],[\\"set\\",\\"test2\\",[\\"nat\\",\\"100\\"]],[\\"if\\",[\\"eq\\",[\\"ext\\",\\"in\\",\\"test1\\",[\\"test2\\"]]],[\\"ext\\",\\"drop\\",\\"test1\\",[\\"test2\\"]]],[\\"set\\",\\"test3\\",[\\"ext\\",\\"length\\",\\"test1\\"]]]}]}", 150 | "config": Object { 151 | "abi_format": "compact", 152 | "macros": true, 153 | "ml_format": "compact", 154 | }, 155 | "ml": "parameter bytes;storage unit;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0x36203e28;COMPARE;EQ;IF{DROP;DROP;NONE nat;NONE nat;PAIR;NONE (set nat);PAIR;PAIR;EMPTY_SET nat;SWAP;SET_CAAR;PUSH nat 100;DIP{PUSH bool True};DIIP{DUP;CAAR};UPDATE;SWAP;SET_CAAR;PUSH nat 200;DIP{PUSH bool True};DIIP{DUP;CAAR};UPDATE;SWAP;SET_CAAR;PUSH nat 300;DIP{PUSH bool True};DIIP{DUP;CAAR};UPDATE;SWAP;SET_CAAR;PUSH nat 100;SWAP;SET_CADAR;DUP;CADAR;DIP{DUP;CAAR};MEM;DIP{PUSH bool True};COMPARE;EQ;IF{DUP;CADAR;DIP{PUSH bool False};DIIP{DUP;CAAR};UPDATE;SWAP;SET_CAAR}{};DUP;CAAR;SIZE;SWAP;SET_CADDR;CDR}{DROP;PUSH nat 400;FAILWITH}}", 156 | } 157 | `; 158 | 159 | exports[`Compiler signature sample compiles correctly 1`] = ` 160 | Object { 161 | "abi": "{\\"entry\\":[{\\"name\\":\\"TestSignature\\",\\"id\\":\\"0xda7f7491\\",\\"input\\":[],\\"temp\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"bool\\"]}],\\"code\\":[[\\"set\\",\\"test1\\",[\\"verify\\",[\\"bytes\\",\\"0x0500\\"],[\\"signature\\",\\"edsigtgjPezMVW9zCu8ny4uMaAsT3gqbRANSNAzVBvZoFXx3ZiPifbLUUthdj4tewcCvJf3HpPfMTnML73hCi6UnFvZpvWsqTBk\\"],[\\"key\\",\\"edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym\\"]]]]}]}", 162 | "config": Object { 163 | "abi_format": "compact", 164 | "macros": true, 165 | "ml_format": "compact", 166 | }, 167 | "ml": "parameter bytes;storage unit;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0xda7f7491;COMPARE;EQ;IF{DROP;DROP;NONE bool;PAIR;PUSH key \\"edpkuk9Z83jy5DJtga82A1MdYNvjVfCi3GfV31dQDtcS3FE8bbhZym\\";DIP{PUSH signature \\"edsigtgjPezMVW9zCu8ny4uMaAsT3gqbRANSNAzVBvZoFXx3ZiPifbLUUthdj4tewcCvJf3HpPfMTnML73hCi6UnFvZpvWsqTBk\\"};DIIP{PUSH bytes 0x0500};CHECK_SIGNATURE;SWAP;SET_CAR;CDR}{DROP;PUSH nat 400;FAILWITH}}", 168 | } 169 | `; 170 | 171 | exports[`Compiler storage sample compiles correctly 1`] = ` 172 | Object { 173 | "abi": "{\\"storage\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"nat\\"]},{\\"name\\":\\"test2\\",\\"type\\":[\\"string\\"]},{\\"name\\":\\"test3\\",\\"type\\":[\\"bytes\\"]}],\\"entry\\":[{\\"name\\":\\"TestStorage\\",\\"id\\":\\"0xed5088d9\\",\\"input\\":[],\\"temp\\":[],\\"code\\":[[\\"set\\",\\"storage.test1\\",[\\"nat\\",\\"1\\"]],[\\"set\\",\\"storage.test2\\",[\\"concat\\",[\\"string\\",\\"test\\"],[\\"string\\",\\"test\\"]]],[\\"set\\",\\"storage.test3\\",[\\"pack\\",[\\"storage.test2\\"]]]]}]}", 174 | "config": Object { 175 | "abi_format": "compact", 176 | "macros": true, 177 | "ml_format": "compact", 178 | }, 179 | "ml": "parameter bytes;storage (pair nat (pair string bytes));code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0xed5088d9;COMPARE;EQ;IF{DROP;DROP;PUSH nat 1;SWAP;SET_CDAR;PUSH string \\"test\\";DIP{PUSH string \\"test\\"};CONCAT;SWAP;SET_CDDAR;DUP;CDDAR;PACK;SWAP;SET_CDDDR}{DROP;PUSH nat 400;FAILWITH}}", 180 | } 181 | `; 182 | 183 | exports[`Compiler string sample compiles correctly 1`] = ` 184 | Object { 185 | "abi": "{\\"entry\\":[{\\"name\\":\\"TestString\\",\\"id\\":\\"0xf8eef9d9\\",\\"input\\":[],\\"temp\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"string\\"]},{\\"name\\":\\"test2\\",\\"type\\":[\\"string\\"]},{\\"name\\":\\"test3\\",\\"type\\":[\\"nat\\"]},{\\"name\\":\\"test4\\",\\"type\\":[\\"string\\"]}],\\"code\\":[[\\"set\\",\\"test1\\",[\\"string\\",\\"Hello World\\"]],[\\"set\\",\\"test2\\",[\\"concat\\",[\\"string\\",\\"Hello World\\"],[\\"test1\\"]]],[\\"set\\",\\"test3\\",[\\"ext\\",\\"length\\",\\"test1\\"]],[\\"set\\",\\"test4\\",[\\"slice\\",[\\"test2\\"],[\\"nat\\",\\"0\\"],[\\"nat\\",\\"10\\"]]]]}]}", 186 | "config": Object { 187 | "abi_format": "compact", 188 | "macros": true, 189 | "ml_format": "compact", 190 | }, 191 | "ml": "parameter bytes;storage unit;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0xf8eef9d9;COMPARE;EQ;IF{DROP;DROP;NONE string;NONE nat;PAIR;NONE string;PAIR;NONE string;PAIR;PAIR;PUSH string \\"Hello World\\";SWAP;SET_CAAR;PUSH string \\"Hello World\\";DIP{DUP;CAAR};CONCAT;SWAP;SET_CADAR;DUP;CAAR;SIZE;SWAP;SET_CADDAR;PUSH nat 0;DIP{PUSH nat 10};DIIP{DUP;CADAR};SLICE;IF_NONE{PUSH string \\"Unable to slice\\";FAILWITH}{};SWAP;SET_CADDDR;CDR}{DROP;PUSH nat 400;FAILWITH}}", 192 | } 193 | `; 194 | 195 | exports[`Compiler timestamp sample compiles correctly 1`] = ` 196 | Object { 197 | "abi": "{\\"entry\\":[{\\"name\\":\\"TestTimestamp\\",\\"id\\":\\"0xb9e54d3e\\",\\"input\\":[],\\"temp\\":[{\\"name\\":\\"test1\\",\\"type\\":[\\"timestamp\\"]},{\\"name\\":\\"test2\\",\\"type\\":[\\"timestamp\\"]},{\\"name\\":\\"test3\\",\\"type\\":[\\"int\\"]},{\\"name\\":\\"test4\\",\\"type\\":[\\"timestamp\\"]}],\\"code\\":[[\\"set\\",\\"test1\\",[\\"NOW\\"]],[\\"set\\",\\"test2\\",[\\"add\\",[\\"test1\\"],[\\"int\\",\\"100\\"]]],[\\"set\\",\\"test3\\",[\\"sub\\",[\\"test2\\"],[\\"test1\\"]]],[\\"set\\",\\"test4\\",[\\"add\\",[\\"int\\",\\"200\\"],[\\"NOW\\"],[\\"sub\\",[\\"NOW\\"],[\\"test1\\"]]]]]}]}", 198 | "config": Object { 199 | "abi_format": "compact", 200 | "macros": true, 201 | "ml_format": "compact", 202 | }, 203 | "ml": "parameter bytes;storage unit;code{DUP;CDR;NIL operation;PAIR;SWAP;CAR;DUP;PUSH nat 4;PUSH nat 0;SLICE;IF_NONE{PUSH nat 100;FAILWITH}{};DUP;PUSH bytes 0xb9e54d3e;COMPARE;EQ;IF{DROP;DROP;NONE timestamp;NONE int;PAIR;NONE timestamp;PAIR;NONE timestamp;PAIR;PAIR;NOW;SWAP;SET_CAAR;DUP;CAAR;DIP{PUSH int 100};ADD;SWAP;SET_CADAR;DUP;CADAR;DIP{DUP;CAAR};SUB;SWAP;SET_CADDAR;PUSH int 200;DIP{NOW};ADD;DIP{NOW;DIP{DUP;CAAR};SUB};ADD;SWAP;SET_CADDDR;CDR}{DROP;PUSH nat 400;FAILWITH}}", 204 | } 205 | `; 206 | --------------------------------------------------------------------------------