1066 lines
70 KiB
JavaScript
1066 lines
70 KiB
JavaScript
(function (EXPORTS) { //floBlockchainAPI v3.1.3
|
|
/* FLO Blockchain Operator to send/receive data from blockchain using API calls via FLO Blockbook*/
|
|
'use strict';
|
|
const floBlockchainAPI = EXPORTS;
|
|
|
|
/* Updating Blockbook */
|
|
const DEFAULT = {
|
|
blockchain: floGlobals.blockchain,
|
|
apiURL: {
|
|
FLO: ['https://blockbook.ranchimall.net/'],
|
|
FLO_TEST: ['https://blockbook-testnet.ranchimall.net/']
|
|
},
|
|
sendAmt: 0.0003,
|
|
fee: 0.0002,
|
|
minChangeAmt: 0.0002,
|
|
receiverID: floGlobals.adminID
|
|
};
|
|
|
|
floBlockchainAPI.apiURL = DEFAULT.apiURL[DEFAULT.blockchain][0];
|
|
const SATOSHI_IN_BTC = 1e8;
|
|
const isUndefined = val => typeof val === 'undefined';
|
|
|
|
const torExitNodes = new Set(["185.241.208.232", "194.26.192.64", "171.25.193.25", "80.67.167.81", "192.42.116.187", "198.98.51.189", "89.58.26.216", "109.70.100.4", "149.56.22.133", "5.45.102.93", "178.17.174.14", "192.42.116.196", "185.220.101.4", "45.141.215.62", "94.102.51.15", "192.42.116.213", "107.189.28.166", "185.241.208.243", "45.141.215.80", "193.26.115.61", "192.42.116.175", "149.56.44.47", "107.189.13.91", "87.118.116.103", "178.17.171.102", "185.243.218.110", "192.42.116.208", "89.58.41.156", "2.58.56.43", "104.192.1.138", "45.95.169.184", "107.189.8.56", "176.58.121.177", "185.220.101.31", "45.141.215.200", "109.70.100.1", "185.244.192.175", "185.129.61.2", "144.172.118.41", "192.42.116.184", "45.151.167.10", "185.220.101.27", "91.203.144.194", "45.141.215.88", "179.43.182.232", "185.220.101.5", "109.70.100.2", "107.189.14.4", "94.16.116.81", "185.220.101.8", "185.220.101.12", "88.80.20.86", "23.154.177.15", "45.141.215.56", "5.42.66.6", "23.129.64.225", "104.244.75.74", "45.95.169.228", "37.187.5.192", "45.141.215.169", "109.70.100.66", "45.79.144.222", "185.227.68.78", "179.43.159.199", "2.57.122.246", "192.42.116.201", "185.220.102.248", "195.176.3.23", "45.138.16.42", "216.73.159.75", "185.165.169.239", "23.129.64.213", "109.70.100.6", "45.80.158.27", "45.138.16.240", "178.20.55.16", "192.42.116.173", "51.15.249.160", "192.42.116.200", "185.220.102.254", "45.141.215.63", "193.218.118.151", "192.42.116.211", "185.100.85.24", "185.195.71.12", "107.189.8.181", "193.189.100.199", "109.70.100.69", "185.100.87.250", "31.220.93.201", "89.236.112.100", "45.141.215.90", "185.35.202.222", "109.70.100.65", "95.142.161.63", "192.42.116.181", "192.42.116.23", "194.26.192.77", "193.189.100.198", "180.150.226.99", "23.129.64.227", "107.189.4.23", "45.141.215.235", "185.220.102.252", "109.70.100.67", "185.220.100.255", "185.220.101.21", "185.100.85.22", "128.31.0.13", "46.182.21.248", "192.42.116.174", "185.241.208.115", "185.220.101.1", "192.42.116.202", "45.141.215.97", "185.243.218.204", "78.142.18.219", "192.42.116.192", "190.120.229.98", "192.42.116.177", "45.138.16.113", "192.42.116.212", "185.220.101.3", "45.138.16.222", "5.42.80.232", "87.118.122.51", "107.189.11.166", "185.220.102.245", "185.220.102.251", "46.182.21.250", "5.255.103.235", "185.243.218.89", "185.193.52.180", "185.220.101.24", "2.57.122.215", "45.15.157.177", "185.220.100.253", "37.48.120.64", "204.8.156.142", "192.42.116.179", "185.220.100.240", "185.241.208.236", "185.195.71.244", "193.105.134.155", "51.15.59.15", "185.100.85.23", "45.151.167.11", "82.197.182.161", "192.42.116.191", "27.255.75.198", "171.25.193.79", "45.95.169.255", "45.138.16.230", "107.189.29.103", "163.172.213.212", "95.143.193.125", "23.154.177.7", "185.220.101.23", "195.176.3.24", "107.189.1.9", "192.42.116.182", "23.137.249.240", "192.42.116.189", "23.129.64.146", "45.138.16.107", "107.189.5.121", "107.189.30.236", "94.16.121.91", "109.70.100.70", "185.254.196.141", "194.15.112.133", "192.42.116.180", "173.249.57.253", "185.220.102.250", "185.100.85.25", "185.220.101.13", "185.220.101.25", "192.42.116.199", "23.154.177.2", "107.189.31.232", "45.141.215.81", "192.42.116.220", "185.67.82.114", "45.141.215.114", "185.243.218.61", "107.189.13.184", "107.189.10.141", "104.244.79.61", "185.106.94.195", "176.126.253.190", "23.154.177.22", "192.42.116.210", "185.220.102.249", "23.184.48.127", "192.42.116.218", "91.208.75.4", "192.42.116.178", "178.175.148.209", "208.109.36.224", "23.137.251.61", "94.142.241.194", "162.251.5.152", "23.154.177.4", "45.138.16.76", "45.9.150.103", "213.252.140.118", "185.243.218.95", "45.134.225.36", "109.70.100.5", "185.243.218.202", "185.220.101.19", "192.42.116.176", "109.70.100.71", "45.151.167.13", "185.220.102.4", "185.220.102.7", "104.244.79.50", "178.17.174.198", "199.195.249.214", "66.146.193.33", "107.189.8.238", "139.99.8.57", "45.141.215.95", "192.42.116.219", "114.199.75.111", "185.220.100.242", "5.42.80.234", "173.237.206.68", "139.99.172.11", "23.129.64.143", "80.241.60.207", "192.42.116.194", "45.95.169.226", "185.220.102.8", "109.70.100.3", "179.43.159.200", "192.42.116.217", "185.220.101.6", "198.98.50.199", "185.100.87.192", "193.189.100.202", "163.172.45.102", "185.220.101.0", "107.189.8.133", "185.129.61.6", "104.244.78.233", "192.42.116.15", "192.42.116.195", "45.141.215.110", "193.189.100.203", "77.48.28.237", "104.244.79.232", "193.26.115.43", "199.195.250.165", "190.211.254.97", "45.141.215.61", "185.220.101.17", "192.42.116.203", "185.220.102.247", "91.132.144.59", "185.141.147.129", "23.129.64.149", "185.183.157.214", "95.211.244.28", "192.42.116.188", "188.214.104.21", "192.42.116.186", "192.42.116.197", "107.189.13.247", "212.73.134.204", "185.235.146.29", "188.68.49.235", "92.205.237.227", "23.154.177.12", "199.195.253.180", "171.25.193.234", "185.241.208.71", "96.66.15.152", "94.16.121.226", "204.85.191.9", "91.210.59.57", "5.255.115.42", "185.220.103.113", "216.239.90.19", "77.91.87.79", "192.42.116.216", "23.154.177.23", "192.42.116.198", "173.255.255.215", "144.217.80.80", "107.189.10.175", "45.95.169.227", "103.251.167.20", "185.220.101.30", "5.255.125.196", "198.98.48.192", "185.220.102.242", "23.154.177.18", "185.86.148.90", "185.142.239.49", "185.220.101.2", "5.255.100.219", "107.189.5.7", "199.195.251.119", "185.220.101.10", "92.246.84.133", "66.220.242.222", "184.105.48.40", "23.129.64.133", "185.130.44.108", "192.42.116.20", "185.181.61.115", "192.42.116.19", "149.202.79.129", "146.59.35.38", "23.154.177.20", "185.191.204.254", "23.154.177.3", "185.233.100.23", "23.154.177.19", "45.92.1.74", "107.189.31.225", "89.58.18.10", "138.59.18.110", "185.246.188.73", "192.42.116.221", "104.244.77.192", "192.42.116.214", "178.170.37.11", "188.68.41.191", "192.42.116.183", "185.220.103.115", "178.175.135.7", "209.141.51.30", "141.98.11.62", "171.25.193.235", "23.137.249.143", "179.43.159.197", "192.99.168.180", "185.220.101.11", "185.243.218.41", "89.234.157.254", "47.243.74.136", "107.189.28.199", "185.129.61.9", "185.220.101.28", "185.220.101.29", "5.255.99.5", "179.43.182.58", "185.129.61.3", "23.129.64.135", "107.189.30.69", "51.15.227.109", "185.207.107.216", "185.129.61.129", "185.100.87.41", "23.129.64.145", "179.43.159.201", "23.129.64.224", "192.42.116.28", "93.99.104.194", "185.244.192.184", "45.95.169.223", "104.244.73.43", "185.56.83.83", "87.120.254.48", "185.185.170.27", "195.88.74.206", "107.174.138.172", "109.70.100.68", "23.129.64.139", "94.230.208.147", "77.91.85.147", "77.81.247.72", "2.58.56.220", "185.220.103.7", "149.202.79.101", "5.255.104.202", "178.175.148.195", "83.96.213.63", "185.100.87.174", "79.137.195.103", "185.220.101.20", "107.189.3.11", "185.220.101.22", "185.220.101.7", "217.12.221.131", "179.43.159.196", "45.95.169.230", "107.189.1.160", "208.109.215.188", "171.25.193.78", "204.194.29.4", "104.244.77.80", "162.247.72.199", "89.58.52.25", "192.42.116.209", "217.146.2.41", "185.220.103.117", "23.154.177.10", "91.208.75.3", "94.230.208.148", "95.128.43.164", "171.25.193.20", "102.130.113.9", "91.92.109.43", "107.189.7.144", "185.220.102.240", "5.255.124.150", "198.98.60.158", "185.227.134.106", "193.233.233.221", "71.19.144.106", "185.84.31.254", "23.129.64.132", "62.171.137.169", "193.189.100.196", "185.220.101.18", "107.189.12.3", "91.208.75.178", "193.35.18.49", "185.246.188.74", "45.132.246.245", "209.141.55.26", "198.98.48.20", "185.129.61.1", "108.61.189.136", "185.220.102.243", "107.189.1.96", "185.100.87.136", "213.95.149.22", "23.129.64.217", "192.42.116.185", "5.45.104.176", "192.42.116.193", "23.154.177.16", "198.98.49.203", "171.25.193.77", "91.208.75.153", "162.247.74.216", "179.43.159.194", "54.36.108.162", "198.98.48.33", "188.68.52.231", "185.220.100.252", "205.185.124.193", "104.244.73.190", "185.100.87.139", "23.154.177.25", "77.105.146.42", "79.137.202.92", "51.38.81.135", "87.118.116.90", "23.129.64.134", "185.246.188.67", "185.129.62.62", "185.220.100.241", "82.221.131.71", "209.141.59.116", "194.195.120.132", "185.207.107.130", "178.218.144.99", "172.104.243.155", "93.99.104.128", "87.118.122.30", "185.100.87.253", "51.195.91.124", "104.192.3.74", "185.252.232.218", "23.129.64.141", "5.196.95.34", "185.220.102.6", "23.184.48.128", "193.239.232.102", "185.220.101.16", "91.203.145.116", "185.129.61.4", "23.129.64.147", "37.228.129.63", "45.151.167.12", "93.95.228.205", "185.220.102.244", "209.141.54.203", "93.95.230.165", "94.142.244.16", "162.247.72.192", "185.146.232.234", "81.16.33.42", "107.189.30.86", "51.81.222.62", "23.154.177.5", "77.220.196.253", "72.167.47.69", "185.220.101.26", "104.219.236.100", "192.42.116.204", "185.246.128.161", "200.122.181.2", "199.195.253.247", "109.201.133.100", "142.44.234.69", "89.147.110.202", "89.185.85.140", "104.244.79.44", "5.2.79.179", "23.129.64.130", "104.244.78.187", "23.154.177.13", "5.255.97.221", "92.205.129.119", "80.82.78.14", "23.154.177.8", "51.38.113.118", "45.61.184.205", "107.189.31.134", "185.220.103.114", "179.48.251.188", "135.125.205.25", "198.98.54.49", "193.189.100.205", "185.220.102.253", "45.79.50.161", "202.69.76.36", "79.137.198.213", "46.166.139.111", "5.255.111.64", "51.89.138.51", "216.73.159.101", "166.70.207.2", "96.27.198.133", "194.15.115.212", "46.234.47.105", "146.59.35.246", "23.137.248.100", "185.220.102.241", "107.189.14.43", "212.95.50.77", "128.127.180.156", "80.67.172.162", "185.129.61.5", "185.129.61.10", "23.129.64.214", "185.220.100.254", "160.119.249.240", "185.243.218.46", "185.220.102.246", "104.244.74.97", "23.129.64.228", "23.129.64.218", "185.220.100.243", "54.36.101.21", "5.255.99.124", "107.189.13.253", "130.149.80.199", "171.25.193.80", "144.24.197.112", "199.195.251.78", "23.129.64.223", "195.80.151.30", "185.7.33.146", "107.189.4.12", "45.95.169.229", "107.189.6.124", "46.38.255.27", "107.189.8.226", "143.42.199.223", "103.251.167.10", "185.34.33.2", "5.255.98.23", "74.82.47.194", "194.163.157.49", "192.42.116.215", "185.220.101.14", "194.15.113.118", "89.147.108.62", "185.220.101.15", "185.42.170.203", "23.154.177.6", "162.247.74.27", "199.195.253.124", "193.189.100.201", "62.182.84.146", "191.101.217.24", "23.129.64.229", "85.93.218.204", "178.17.174.164", "205.185.117.149", "193.218.118.133", "23.154.177.21", "5.255.101.10", "82.221.131.5", "193.189.100.204", "103.196.37.111", "103.109.101.105", "192.42.116.18", "23.129.64.226", "107.189.13.251", "45.56.81.190", "192.42.116.13", "107.189.11.111", "198.46.166.157", "185.220.103.119", "54.38.183.101", "77.68.20.217", "185.220.101.36", "103.236.201.88", "162.247.74.213", "185.129.61.8", "89.147.110.154", "45.95.169.225", "141.239.149.94", "82.221.128.191", "72.14.179.10", "46.232.251.191", "23.129.64.215", "162.247.74.7", "23.154.177.14", "89.147.109.226", "193.41.226.117", "89.147.108.209", "23.129.64.137", "93.123.12.112", "185.14.97.37", "103.163.218.11", "23.129.64.131", "23.129.64.142", "23.137.249.185", "89.58.41.251", "185.220.101.9", "202.182.99.129", "205.185.119.35", "193.189.100.194", "204.85.191.8", "185.56.171.94", "23.129.64.144", "102.130.127.117", "192.42.116.24", "179.43.159.198", "185.38.175.133", "185.220.101.39", "193.168.143.129", "5.255.127.222", "95.211.210.103", "185.220.103.116", "23.129.64.211", "23.129.64.220", "185.113.128.30", "151.80.148.159", "192.99.149.111", "23.129.64.210", "37.228.129.128", "91.208.75.239", "185.220.103.120", "185.165.171.84", "193.105.134.150", "209.141.46.203", "209.141.50.178", "104.244.74.23", "45.95.169.224", "23.129.64.140", "176.118.193.33", "204.85.191.7", "104.244.73.193", "162.247.74.204", "91.208.75.156", "205.185.116.34", "125.212.241.131", "5.2.72.110", "179.43.159.195", "185.154.110.142", "91.206.26.26", "45.79.177.21", "23.154.177.9", "193.189.100.197", "46.165.243.36", "107.189.2.108", "23.154.177.17", "23.129.64.148", "5.45.98.162", "5.255.101.131", "23.129.64.136", "107.189.31.33", "185.82.219.109", "104.244.73.136", "185.129.61.7", "5.255.115.58", "23.154.177.24", "165.73.242.163", "193.189.100.200", "192.46.227.185", "5.196.8.113", "77.91.86.95", "85.209.176.103", "23.137.249.8", "5.255.98.151", "23.129.64.221", "23.129.64.219", "23.129.64.216", "185.243.218.35", "104.244.77.208", "94.228.169.70", "51.75.64.23", "176.58.100.98", "23.154.177.11", "23.129.64.138", "143.42.110.237", "94.16.112.22", "144.172.118.4", "185.130.47.58", "185.154.110.17", "104.244.72.132", "5.2.79.190", "23.129.64.212", "109.169.33.163", "5.2.67.226", "109.69.67.17", "108.181.27.205", "5.255.103.190", "107.189.14.106", "5.255.99.147", "193.189.100.206", "193.218.118.182", "185.181.61.142", "23.129.64.222", "193.35.18.77", "185.100.86.128", "91.203.5.118", "83.97.20.77", "45.138.16.203", "2.57.122.58", "185.181.61.18", "195.176.3.19", "195.176.3.20", "198.58.107.53", "138.128.222.68", "118.163.74.160", "185.241.208.54", "38.97.116.244", "104.244.77.79", "103.253.24.18", "185.225.69.203", "162.247.74.206", "79.124.8.241", "91.203.5.115", "144.172.118.102", "144.172.118.124", "185.225.69.232", "163.5.143.76", "144.172.118.51", "178.20.55.182", "109.104.153.22", "193.233.133.109", "51.158.115.62", "92.205.31.137", "185.193.158.134", "217.12.215.167", "45.15.158.39", "185.174.136.114", "91.219.239.166", "91.219.237.56", "51.159.211.57", "192.210.255.181", "185.170.114.25", "205.185.123.93", "205.185.121.170", "107.189.13.180", "104.244.78.162", "104.244.76.170", "104.244.74.57", "195.160.220.104", "31.220.98.139", "158.220.92.203", "23.184.48.101", "178.31.22.116", "79.102.34.63", "185.220.103.5", "179.43.128.16", "45.128.133.242", "185.220.103.118", "185.100.85.132", "107.189.7.48", "5.135.174.211", "45.8.22.207", "185.220.101.159", "185.220.101.141", "185.220.101.134", "185.220.101.147", "185.220.101.153", "185.220.101.145", "185.220.101.158", "185.220.101.160", "185.220.101.137", "185.220.101.140", "185.220.101.132", "185.220.101.157", "185.220.101.150", "185.220.101.143", "158.69.201.47", "107.189.1.175", "176.58.89.182", "185.220.101.138", "82.118.242.158", "217.170.201.71", "193.189.100.195", "144.172.118.48", "185.220.101.135", "185.220.101.191", "185.220.101.136", "185.220.101.179", "185.220.101.170", "185.220.101.149", "185.220.101.173", "185.220.101.171", "185.220.101.161", "185.220.101.163", "185.220.101.152", "185.220.101.162", "185.220.101.176", "185.220.101.188", "185.82.127.128", "85.235.145.205", "172.81.131.139", "5.255.100.26", "62.63.244.7", "104.219.236.101", "23.137.248.139", "185.241.208.204", "45.141.215.111", "185.241.208.202", "45.141.215.21", "45.61.185.172", "185.241.208.206", "205.185.113.180", "93.242.68.75", "185.220.100.248", "185.220.100.251", "185.220.100.247", "185.220.100.245", "185.220.100.246", "185.220.100.249", "185.220.100.250", "185.220.100.244", "77.72.85.30", "51.222.142.67", "107.172.31.165", "107.174.231.197", "198.144.178.163", "23.137.250.34", "107.172.13.143", "107.172.31.146", "173.232.195.137", "50.3.182.156", "173.232.195.144", "173.232.195.146", "172.81.131.168", "172.81.131.84", "77.48.28.239", "172.81.131.156", "185.183.159.40", "196.189.30.114", "107.189.8.5", "185.220.101.168", "185.220.101.165", "185.220.101.142", "185.220.101.167", "185.220.101.166", "185.220.101.169", "77.48.28.193", "37.228.129.5", "144.172.73.11", "107.189.14.57", "84.16.224.227", "185.220.103.4", "162.247.74.202", "185.220.103.6", "162.247.74.200", "185.220.103.9", "185.220.103.8", "154.12.254.57", "94.103.124.184", "185.220.101.189", "67.219.109.141", "185.220.101.187", "185.220.101.186", "185.220.101.183", "50.3.182.133", "185.220.101.182", "185.220.101.184", "188.172.229.15", "89.58.18.210", "45.9.150.130", "190.103.179.98", "108.181.124.143", "178.218.144.51", "185.220.101.66", "185.220.101.70", "185.220.101.68", "185.220.101.77", "185.220.101.78", "185.220.101.81", "185.220.101.71", "185.220.101.83", "185.220.101.75", "185.220.101.85", "185.220.101.73", "185.220.101.82", "185.220.101.65", "185.220.101.84", "185.220.101.76", "185.220.101.86", "185.220.101.69", "185.220.101.67", "185.220.101.80", "185.220.101.64", "185.220.101.74", "185.220.101.79", "185.220.101.72", "185.220.101.87", "199.249.230.120", "184.75.221.171", "5.182.86.212", "104.244.72.115", "198.23.133.132", "23.94.36.142", "198.98.60.90", "84.19.182.20", "45.9.148.219", "217.160.88.146", "104.219.232.126", "45.139.122.241", "199.195.253.156", "75.119.142.240", "199.249.230.103", "199.249.230.104", "199.249.230.116", "199.249.230.101", "199.249.230.119", "199.249.230.100", "199.249.230.102", "199.249.230.109", "199.249.230.81", "199.249.230.176", "199.249.230.79", "199.249.230.167", "199.249.230.88", "199.249.230.188", "199.249.230.80", "199.249.230.144", "199.249.230.78", "199.249.230.111", "199.249.230.68", "199.249.230.180", "199.249.230.150", "199.249.230.70", "199.249.230.77", "199.249.230.112", "199.249.230.65", "199.249.230.183", "199.249.230.189", "199.249.230.178", "199.249.230.145", "199.249.230.115", "199.249.230.147", "199.249.230.66", "199.249.230.140", "199.249.230.114", "199.249.230.170", "199.249.230.71", "199.249.230.148", "199.249.230.67", "199.249.230.75", "199.249.230.146", "199.249.230.151", "199.249.230.187", "199.249.230.174", "199.249.230.143", "199.249.230.118", "199.249.230.64", "199.249.230.85", "199.249.230.113", "199.249.230.155", "199.249.230.153", "199.249.230.89", "45.77.67.251", "123.253.35.32", "45.83.104.137", "94.32.66.15", "185.220.101.181", "185.220.101.178", "185.220.101.177", "185.220.101.175", "185.220.101.172", "94.16.116.86", "5.181.80.107", "198.50.207.20", "107.189.7.168", "85.215.76.62", "185.247.184.105", "178.236.247.122", "109.107.190.171", "193.233.233.124", "193.218.118.188", "2.58.95.45", "45.154.98.102", "92.205.185.52", "92.205.163.226", "185.217.125.210", "5.255.118.104", "212.69.167.80", "23.137.249.227", "5.255.118.244", "71.19.148.129", "143.42.114.46", "45.33.15.243", "104.237.158.32", "172.232.161.205", "172.232.161.206", "74.207.248.172", "172.233.209.179", "45.66.35.21", "45.66.35.35", "45.66.35.10", "45.66.35.20", "45.66.35.22", "51.210.138.64", "130.204.161.3", "175.214.127.6", "31.220.85.162", "198.96.155.3", "50.118.225.160", "45.135.132.20", "23.152.24.77", "45.95.169.99", "94.75.225.81", "37.228.129.131", "23.137.249.62", "103.172.134.26", "199.249.230.121", "191.252.111.55", "35.0.127.52", "185.129.62.63", "23.94.211.25", "185.220.101.139", "185.220.101.144", "185.220.101.130", "185.220.101.156", "185.220.101.128", "185.220.101.131", "185.220.101.154", "185.220.101.164", "185.220.101.180", "185.220.101.155", "185.220.101.133", "185.220.101.190", "185.220.101.151", "185.220.101.174", "185.220.101.148", "185.220.101.129", "185.220.101.185", "37.221.208.68", "87.120.254.132", "5.255.106.9", "45.15.158.165", "193.35.18.105", "178.17.170.23", "185.146.232.243", "194.163.178.164", "94.140.115.63", "37.228.129.24", "81.0.248.210", "193.35.18.98", "45.128.232.170", "193.35.18.96", "45.128.232.102", "193.35.18.94", "193.35.18.95", "149.102.128.242", "89.187.143.31", "193.239.232.228", "103.208.86.5", "193.35.18.120", "185.130.44.43", "185.219.142.126", "37.1.201.144", "5.255.99.108", "85.204.116.211", "130.193.10.21", "130.193.15.79", "84.239.46.144", "178.218.162.62", "199.249.230.122", "199.249.230.84", "45.141.202.164", "199.249.230.74", "148.113.2.107", "199.249.230.105", "199.249.230.73", "199.249.230.110", "199.249.230.72", "199.249.230.86", "103.129.222.46", "64.5.123.66", "185.239.71.160", "5.42.80.233", "5.42.80.235", "200.25.27.112", "46.226.107.206", "103.106.3.175", "96.42.26.63", "192.42.116.26", "192.42.116.17", "192.42.116.14", "192.42.116.22", "192.42.116.25", "192.42.116.27", "74.208.106.128", "213.232.235.83", "91.208.197.144", "51.195.166.174", "198.98.53.136", "157.143.80.38", "198.50.128.237", "193.233.232.86", "144.126.152.77", "158.220.80.216", "154.16.116.61", "45.88.223.151", "144.126.132.30", "89.147.110.214", "89.163.155.136", "107.189.13.93", "77.232.143.255", "77.232.143.243", "77.232.143.248", "94.228.163.25", "199.249.230.186", "199.249.230.177", "199.249.230.159", "199.249.230.161", "199.249.230.163", "199.249.230.149", "199.249.230.154", "199.249.230.164", "199.249.230.160", "199.249.230.173", "199.249.230.158", "199.249.230.157", "199.249.230.108", "199.249.230.83", "199.249.230.168", "199.249.230.82", "199.249.230.166", "199.249.230.123", "199.249.230.106", "199.249.230.76", "199.249.230.117", "199.249.230.169", "199.249.230.171", "199.249.230.175", "199.249.230.107", "199.249.230.152", "199.249.230.162", "2.58.95.53", "199.249.230.69", "2.58.95.47", "2.58.95.59", "2.58.95.56", "178.175.142.26", "199.249.230.156", "199.249.230.87", "103.28.52.93", "185.107.70.56", "89.147.108.56", "148.113.2.104", "38.242.203.135", "162.247.74.201", "172.232.238.10", "5.255.98.198", "5.255.98.231", "23.137.249.150", "149.102.155.205", "199.249.230.179", "199.249.230.165", "199.249.230.182", "199.249.230.184", "199.249.230.142", "136.243.147.59", "199.249.230.185", "185.220.101.89", "149.102.145.222", "185.220.101.90", "185.220.101.88", "87.118.110.27", "37.48.70.156", "185.165.190.111", "5.255.125.153", "205.185.124.176", "107.189.14.41", "93.95.228.81", "172.81.131.140", "185.38.142.4", "95.168.173.143", "178.218.144.18", "189.147.238.226", "189.147.187.10", "189.147.242.169", "104.219.236.93", "161.35.129.51", "86.104.194.13", "104.244.74.159", "185.220.101.40", "185.220.101.32", "185.220.101.38", "185.220.101.37", "185.220.101.35", "185.220.101.33", "185.220.101.34", "185.220.101.41", "185.220.101.42", "205.185.127.100", "185.220.101.57", "185.220.101.43", "185.220.101.46", "185.220.101.58", "185.220.101.61", "185.220.101.60", "185.220.101.63", "185.220.101.54", "185.220.101.52", "185.220.101.62", "185.220.101.56", "185.220.101.44", "185.220.101.49", "5.255.117.56", "185.220.101.55", "185.220.101.45", "185.220.101.53", "185.220.101.59", "185.220.101.51", "185.220.101.48", "185.220.101.47", "185.220.101.50", "95.111.238.0", "152.89.233.169", "89.147.110.82", "176.58.117.81", "23.155.8.104", "51.89.153.112", "5.61.51.143", "5.135.174.213", "37.120.166.23", "37.252.255.135", "82.153.138.48", "185.81.115.120", "45.139.122.176", "84.211.225.54", "31.220.87.46", "144.172.73.6", "51.89.200.109", "212.44.107.82", "89.147.111.124", "94.177.106.59", "94.177.106.55", "94.177.106.46", "93.95.231.88", "152.32.238.235", "74.208.96.95", "38.242.239.62", "87.118.114.44", "80.78.25.9", "185.193.125.95", "107.173.179.59", "179.43.159.78", "81.17.28.95", "45.79.129.209", "82.221.139.190", "107.189.13.254", "81.19.137.127", "149.102.129.11", "81.0.218.34", "93.90.74.31", "51.81.254.4", "109.123.231.55", "185.196.8.2", "158.220.81.45", "62.149.23.133", "158.220.81.47", "158.220.81.78", "209.141.51.180", "176.121.81.51", "178.17.170.184", "202.61.226.98", "202.139.229.157", "89.147.111.106"]);
|
|
const checkIfTor = floBlockchainAPI.checkIfTor = () => {
|
|
return fetch('https://api.ipify.org?format=json').then(response => response.json())
|
|
.then(result => {
|
|
return torExitNodes.has(result.ip)
|
|
}).catch(e => {
|
|
console.error(e)
|
|
return false
|
|
})
|
|
}
|
|
let isTor = false;
|
|
checkIfTor().then(result => {
|
|
isTor = result
|
|
if (isTor) {
|
|
DEFAULT.apiURL.FLO.push('http://kvrddx6heo47rbbt77etxg6litckacbgos3nv5z7vc23ol2kjjeq72id.onion/')
|
|
// DEFAULT.apiURL.FLO_TEST.push('http://omwkzk6bd6zuragdqsrhdyzgxzre7yx4vzrou4vzftintzc2dmagp6qd.onion:15017/')
|
|
}
|
|
});
|
|
|
|
const util = floBlockchainAPI.util = {};
|
|
|
|
util.Sat_to_FLO = value => parseFloat((value / SATOSHI_IN_BTC).toFixed(8));
|
|
util.FLO_to_Sat = value => parseInt(value * SATOSHI_IN_BTC);
|
|
util.toFixed = value => parseFloat((value).toFixed(8));
|
|
|
|
Object.defineProperties(floBlockchainAPI, {
|
|
sendAmt: {
|
|
get: () => DEFAULT.sendAmt,
|
|
set: amt => !isNaN(amt) ? DEFAULT.sendAmt = amt : null
|
|
},
|
|
fee: {
|
|
get: () => DEFAULT.fee,
|
|
set: fee => !isNaN(fee) ? DEFAULT.fee = fee : null
|
|
},
|
|
defaultReceiver: {
|
|
get: () => DEFAULT.receiverID,
|
|
set: floID => DEFAULT.receiverID = floID
|
|
},
|
|
blockchain: {
|
|
get: () => DEFAULT.blockchain
|
|
}
|
|
});
|
|
|
|
if (floGlobals.sendAmt) floBlockchainAPI.sendAmt = floGlobals.sendAmt;
|
|
if (floGlobals.fee) floBlockchainAPI.fee = floGlobals.fee;
|
|
|
|
Object.defineProperties(floGlobals, {
|
|
sendAmt: {
|
|
get: () => DEFAULT.sendAmt,
|
|
set: amt => !isNaN(amt) ? DEFAULT.sendAmt = amt : null
|
|
},
|
|
fee: {
|
|
get: () => DEFAULT.fee,
|
|
set: fee => !isNaN(fee) ? DEFAULT.fee = fee : null
|
|
}
|
|
});
|
|
|
|
const allServerList = new Set(floGlobals.apiURL && floGlobals.apiURL[DEFAULT.blockchain] ? floGlobals.apiURL[DEFAULT.blockchain] : DEFAULT.apiURL[DEFAULT.blockchain]);
|
|
|
|
var serverList = Array.from(allServerList);
|
|
var curPos = floCrypto.randInt(0, serverList.length - 1);
|
|
|
|
function fetch_retry(apicall, rm_node) {
|
|
return new Promise((resolve, reject) => {
|
|
let i = serverList.indexOf(rm_node)
|
|
if (i != -1) serverList.splice(i, 1);
|
|
curPos = floCrypto.randInt(0, serverList.length - 1);
|
|
fetch_api(apicall, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error));
|
|
})
|
|
}
|
|
|
|
function fetch_api(apicall, ic = true) {
|
|
return new Promise((resolve, reject) => {
|
|
if (serverList.length === 0) {
|
|
if (ic) {
|
|
serverList = Array.from(allServerList);
|
|
curPos = floCrypto.randInt(0, serverList.length - 1);
|
|
fetch_api(apicall, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error));
|
|
} else
|
|
reject("No FLO blockbook server working");
|
|
} else {
|
|
let serverURL = serverList[curPos];
|
|
fetch(serverURL + apicall).then(response => {
|
|
if (response.ok)
|
|
response.json().then(data => resolve(data));
|
|
else {
|
|
fetch_retry(apicall, serverURL)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error));
|
|
}
|
|
}).catch(error => {
|
|
fetch_retry(apicall, serverURL)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error));
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
Object.defineProperties(floBlockchainAPI, {
|
|
serverList: {
|
|
get: () => Array.from(serverList)
|
|
},
|
|
current_server: {
|
|
get: () => serverList[curPos]
|
|
}
|
|
});
|
|
|
|
//Promised function to get data from API
|
|
const promisedAPI = floBlockchainAPI.promisedAPI = floBlockchainAPI.fetch = function (apicall, query_params = undefined) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!isUndefined(query_params))
|
|
apicall += '?' + new URLSearchParams(JSON.parse(JSON.stringify(query_params))).toString();
|
|
//console.debug(apicall);
|
|
fetch_api(apicall)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error));
|
|
});
|
|
}
|
|
|
|
//Get balance for the given Address
|
|
const getBalance = floBlockchainAPI.getBalance = function (addr) {
|
|
return new Promise((resolve, reject) => {
|
|
let api = `api/address/${addr}`;
|
|
promisedAPI(api, { details: "basic" })
|
|
.then(result => resolve(result["balance"]))
|
|
.catch(error => reject(error))
|
|
});
|
|
}
|
|
|
|
function getScriptPubKey(address) {
|
|
var tx = bitjs.transaction();
|
|
tx.addoutput(address, 0);
|
|
let outputBuffer = tx.outputs.pop().script;
|
|
return Crypto.util.bytesToHex(outputBuffer)
|
|
}
|
|
|
|
const getUTXOs = address => new Promise((resolve, reject) => {
|
|
promisedAPI(`api/utxo/${address}`, { confirmed: true }).then(utxos => {
|
|
let scriptPubKey = getScriptPubKey(address);
|
|
utxos.forEach(u => u.scriptPubKey = scriptPubKey);
|
|
resolve(utxos);
|
|
}).catch(error => reject(error))
|
|
})
|
|
|
|
//create a transaction with single sender
|
|
const createTx = function (senderAddr, receiverAddr, sendAmt, floData = '', strict_utxo = true) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!floCrypto.validateASCII(floData))
|
|
return reject("Invalid FLO_Data: only printable ASCII characters are allowed");
|
|
else if (!floCrypto.validateFloID(senderAddr, true))
|
|
return reject(`Invalid address : ${senderAddr}`);
|
|
else if (!floCrypto.validateFloID(receiverAddr))
|
|
return reject(`Invalid address : ${receiverAddr}`);
|
|
else if (typeof sendAmt !== 'number' || sendAmt <= 0)
|
|
return reject(`Invalid sendAmt : ${sendAmt}`);
|
|
|
|
getBalance(senderAddr).then(balance => {
|
|
var fee = DEFAULT.fee;
|
|
if (balance < sendAmt + fee)
|
|
return reject("Insufficient FLO balance!");
|
|
getUTXOs(senderAddr).then(utxos => {
|
|
//form/construct the transaction data
|
|
var trx = bitjs.transaction();
|
|
var utxoAmt = 0.0;
|
|
for (var i = utxos.length - 1;
|
|
(i >= 0) && (utxoAmt < sendAmt + fee); i--) {
|
|
//use only utxos with confirmations (strict_utxo mode)
|
|
if (utxos[i].confirmations || !strict_utxo) {
|
|
trx.addinput(utxos[i].txid, utxos[i].vout, utxos[i].scriptPubKey);
|
|
utxoAmt += utxos[i].amount;
|
|
};
|
|
}
|
|
if (utxoAmt < sendAmt + fee)
|
|
reject("Insufficient FLO: Some UTXOs are unconfirmed");
|
|
else {
|
|
trx.addoutput(receiverAddr, sendAmt);
|
|
var change = utxoAmt - sendAmt - fee;
|
|
if (change > DEFAULT.minChangeAmt)
|
|
trx.addoutput(senderAddr, change);
|
|
trx.addflodata(floData.replace(/\n/g, ' '));
|
|
resolve(trx);
|
|
}
|
|
}).catch(error => reject(error))
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
floBlockchainAPI.createTx = function (senderAddr, receiverAddr, sendAmt, floData = '', strict_utxo = true) {
|
|
return new Promise((resolve, reject) => {
|
|
createTx(senderAddr, receiverAddr, sendAmt, floData, strict_utxo)
|
|
.then(trx => resolve(trx.serialize()))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
//Send Tx to blockchain
|
|
const sendTx = floBlockchainAPI.sendTx = function (senderAddr, receiverAddr, sendAmt, privKey, floData = '', strict_utxo = true) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!floCrypto.validateFloID(senderAddr, true))
|
|
return reject(`Invalid address : ${senderAddr}`);
|
|
else if (privKey.length < 1 || !floCrypto.verifyPrivKey(privKey, senderAddr))
|
|
return reject("Invalid Private key!");
|
|
createTx(senderAddr, receiverAddr, sendAmt, floData, strict_utxo).then(trx => {
|
|
var signedTxHash = trx.sign(privKey, 1);
|
|
broadcastTx(signedTxHash)
|
|
.then(txid => resolve(txid))
|
|
.catch(error => reject(error))
|
|
}).catch(error => reject(error))
|
|
});
|
|
}
|
|
|
|
//Write Data into blockchain
|
|
floBlockchainAPI.writeData = function (senderAddr, data, privKey, receiverAddr = DEFAULT.receiverID, options = {}) {
|
|
let strict_utxo = options.strict_utxo === false ? false : true,
|
|
sendAmt = isNaN(options.sendAmt) ? DEFAULT.sendAmt : options.sendAmt;
|
|
return new Promise((resolve, reject) => {
|
|
if (typeof data != "string")
|
|
data = JSON.stringify(data);
|
|
sendTx(senderAddr, receiverAddr, sendAmt, privKey, data, strict_utxo)
|
|
.then(txid => resolve(txid))
|
|
.catch(error => reject(error));
|
|
});
|
|
}
|
|
|
|
//merge all UTXOs of a given floID into a single UTXO
|
|
floBlockchainAPI.mergeUTXOs = function (floID, privKey, floData = '') {
|
|
return new Promise((resolve, reject) => {
|
|
if (!floCrypto.validateFloID(floID, true))
|
|
return reject(`Invalid floID`);
|
|
if (!floCrypto.verifyPrivKey(privKey, floID))
|
|
return reject("Invalid Private Key");
|
|
if (!floCrypto.validateASCII(floData))
|
|
return reject("Invalid FLO_Data: only printable ASCII characters are allowed");
|
|
var trx = bitjs.transaction();
|
|
var utxoAmt = 0.0;
|
|
var fee = DEFAULT.fee;
|
|
getUTXOs(floID).then(utxos => {
|
|
for (var i = utxos.length - 1; i >= 0; i--)
|
|
if (utxos[i].confirmations) {
|
|
trx.addinput(utxos[i].txid, utxos[i].vout, utxos[i].scriptPubKey);
|
|
utxoAmt += utxos[i].amount;
|
|
}
|
|
trx.addoutput(floID, utxoAmt - fee);
|
|
trx.addflodata(floData.replace(/\n/g, ' '));
|
|
var signedTxHash = trx.sign(privKey, 1);
|
|
broadcastTx(signedTxHash)
|
|
.then(txid => resolve(txid))
|
|
.catch(error => reject(error))
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
//split sufficient UTXOs of a given floID for a parallel sending
|
|
floBlockchainAPI.splitUTXOs = function (floID, privKey, count, floData = '') {
|
|
return new Promise((resolve, reject) => {
|
|
if (!floCrypto.validateFloID(floID, true))
|
|
return reject(`Invalid floID`);
|
|
if (!floCrypto.verifyPrivKey(privKey, floID))
|
|
return reject("Invalid Private Key");
|
|
if (!floCrypto.validateASCII(floData))
|
|
return reject("Invalid FLO_Data: only printable ASCII characters are allowed");
|
|
var fee = DEFAULT.fee;
|
|
var splitAmt = DEFAULT.sendAmt + fee;
|
|
var totalAmt = splitAmt * count;
|
|
getBalance(floID).then(balance => {
|
|
var fee = DEFAULT.fee;
|
|
if (balance < totalAmt + fee)
|
|
return reject("Insufficient FLO balance!");
|
|
//get unconfirmed tx list
|
|
getUTXOs(floID).then(utxos => {
|
|
var trx = bitjs.transaction();
|
|
var utxoAmt = 0.0;
|
|
for (let i = utxos.length - 1; (i >= 0) && (utxoAmt < totalAmt + fee); i--) {
|
|
//use only utxos with confirmations (strict_utxo mode)
|
|
if (utxos[i].confirmations || !strict_utxo) {
|
|
trx.addinput(utxos[i].txid, utxos[i].vout, utxos[i].scriptPubKey);
|
|
utxoAmt += utxos[i].amount;
|
|
};
|
|
}
|
|
if (utxoAmt < totalAmt + fee)
|
|
reject("Insufficient FLO: Some UTXOs are unconfirmed");
|
|
else {
|
|
for (let i = 0; i < count; i++)
|
|
trx.addoutput(floID, splitAmt);
|
|
var change = utxoAmt - totalAmt - fee;
|
|
if (change > DEFAULT.minChangeAmt)
|
|
trx.addoutput(floID, change);
|
|
trx.addflodata(floData.replace(/\n/g, ' '));
|
|
var signedTxHash = trx.sign(privKey, 1);
|
|
broadcastTx(signedTxHash)
|
|
.then(txid => resolve(txid))
|
|
.catch(error => reject(error))
|
|
}
|
|
}).catch(error => reject(error))
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
/**Write data into blockchain from (and/or) to multiple floID
|
|
* @param {Array} senderPrivKeys List of sender private-keys
|
|
* @param {string} data FLO data of the txn
|
|
* @param {Array} receivers List of receivers
|
|
* @param {boolean} preserveRatio (optional) preserve ratio or equal contribution
|
|
* @return {Promise}
|
|
*/
|
|
floBlockchainAPI.writeDataMultiple = function (senderPrivKeys, data, receivers = [DEFAULT.receiverID], options = {}) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!Array.isArray(senderPrivKeys))
|
|
return reject("Invalid senderPrivKeys: SenderPrivKeys must be Array");
|
|
if (options.preserveRatio === false) {
|
|
let tmp = {};
|
|
let amount = (DEFAULT.sendAmt * receivers.length) / senderPrivKeys.length;
|
|
senderPrivKeys.forEach(key => tmp[key] = amount);
|
|
senderPrivKeys = tmp;
|
|
}
|
|
if (!Array.isArray(receivers))
|
|
return reject("Invalid receivers: Receivers must be Array");
|
|
else {
|
|
let tmp = {};
|
|
let amount = options.sendAmt || DEFAULT.sendAmt;
|
|
receivers.forEach(floID => tmp[floID] = amount);
|
|
receivers = tmp
|
|
}
|
|
if (typeof data != "string")
|
|
data = JSON.stringify(data);
|
|
sendTxMultiple(senderPrivKeys, receivers, data)
|
|
.then(txid => resolve(txid))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
/**Send Tx from (and/or) to multiple floID
|
|
* @param {Array or Object} senderPrivKeys List of sender private-key (optional: with coins to be sent)
|
|
* @param {Object} receivers List of receivers with respective amount to be sent
|
|
* @param {string} floData FLO data of the txn
|
|
* @return {Promise}
|
|
*/
|
|
const sendTxMultiple = floBlockchainAPI.sendTxMultiple = function (senderPrivKeys, receivers, floData = '') {
|
|
return new Promise((resolve, reject) => {
|
|
if (!floCrypto.validateASCII(floData))
|
|
return reject("Invalid FLO_Data: only printable ASCII characters are allowed");
|
|
let senders = {},
|
|
preserveRatio;
|
|
//check for argument validations
|
|
try {
|
|
let invalids = {
|
|
InvalidSenderPrivKeys: [],
|
|
InvalidSenderAmountFor: [],
|
|
InvalidReceiverIDs: [],
|
|
InvalidReceiveAmountFor: []
|
|
}
|
|
let inputVal = 0,
|
|
outputVal = 0;
|
|
//Validate sender privatekeys (and send amount if passed)
|
|
//conversion when only privateKeys are passed (preserveRatio mode)
|
|
if (Array.isArray(senderPrivKeys)) {
|
|
senderPrivKeys.forEach(key => {
|
|
try {
|
|
if (!key)
|
|
invalids.InvalidSenderPrivKeys.push(key);
|
|
else {
|
|
let floID = floCrypto.getFloID(key);
|
|
senders[floID] = {
|
|
wif: key
|
|
}
|
|
}
|
|
} catch (error) {
|
|
invalids.InvalidSenderPrivKeys.push(key)
|
|
}
|
|
})
|
|
preserveRatio = true;
|
|
}
|
|
//conversion when privatekeys are passed with send amount
|
|
else {
|
|
for (let key in senderPrivKeys) {
|
|
try {
|
|
if (!key)
|
|
invalids.InvalidSenderPrivKeys.push(key);
|
|
else {
|
|
if (typeof senderPrivKeys[key] !== 'number' || senderPrivKeys[key] <= 0)
|
|
invalids.InvalidSenderAmountFor.push(key);
|
|
else
|
|
inputVal += senderPrivKeys[key];
|
|
let floID = floCrypto.getFloID(key);
|
|
senders[floID] = {
|
|
wif: key,
|
|
coins: senderPrivKeys[key]
|
|
}
|
|
}
|
|
} catch (error) {
|
|
invalids.InvalidSenderPrivKeys.push(key)
|
|
}
|
|
}
|
|
preserveRatio = false;
|
|
}
|
|
//Validate the receiver IDs and receive amount
|
|
for (let floID in receivers) {
|
|
if (!floCrypto.validateFloID(floID))
|
|
invalids.InvalidReceiverIDs.push(floID);
|
|
if (typeof receivers[floID] !== 'number' || receivers[floID] <= 0)
|
|
invalids.InvalidReceiveAmountFor.push(floID);
|
|
else
|
|
outputVal += receivers[floID];
|
|
}
|
|
//Reject if any invalids are found
|
|
for (let i in invalids)
|
|
if (!invalids[i].length)
|
|
delete invalids[i];
|
|
if (Object.keys(invalids).length)
|
|
return reject(invalids);
|
|
//Reject if given inputVal and outputVal are not equal
|
|
if (!preserveRatio && inputVal != outputVal)
|
|
return reject(`Input Amount (${inputVal}) not equal to Output Amount (${outputVal})`);
|
|
} catch (error) {
|
|
return reject(error)
|
|
}
|
|
//Get balance of senders
|
|
let promises = [];
|
|
for (let floID in senders)
|
|
promises.push(getBalance(floID));
|
|
Promise.all(promises).then(results => {
|
|
let totalBalance = 0,
|
|
totalFee = DEFAULT.fee,
|
|
balance = {};
|
|
//Divide fee among sender if not for preserveRatio
|
|
if (!preserveRatio)
|
|
var dividedFee = totalFee / Object.keys(senders).length;
|
|
//Check if balance of each sender is sufficient enough
|
|
let insufficient = [];
|
|
for (let floID in senders) {
|
|
balance[floID] = parseFloat(results.shift());
|
|
if (isNaN(balance[floID]) || (preserveRatio && balance[floID] <= totalFee) ||
|
|
(!preserveRatio && balance[floID] < senders[floID].coins + dividedFee))
|
|
insufficient.push(floID);
|
|
totalBalance += balance[floID];
|
|
}
|
|
if (insufficient.length)
|
|
return reject({
|
|
InsufficientBalance: insufficient
|
|
})
|
|
//Calculate totalSentAmount and check if totalBalance is sufficient
|
|
let totalSendAmt = totalFee;
|
|
for (let floID in receivers)
|
|
totalSendAmt += receivers[floID];
|
|
if (totalBalance < totalSendAmt)
|
|
return reject("Insufficient total Balance");
|
|
//Get the UTXOs of the senders
|
|
let promises = [];
|
|
for (let floID in senders)
|
|
promises.push(getUTXOs(floID));
|
|
Promise.all(promises).then(results => {
|
|
var trx = bitjs.transaction();
|
|
for (let floID in senders) {
|
|
let utxos = results.shift();
|
|
let sendAmt;
|
|
if (preserveRatio) {
|
|
let ratio = (balance[floID] / totalBalance);
|
|
sendAmt = totalSendAmt * ratio;
|
|
} else
|
|
sendAmt = senders[floID].coins + dividedFee;
|
|
let utxoAmt = 0.0;
|
|
for (let i = utxos.length - 1;
|
|
(i >= 0) && (utxoAmt < sendAmt); i--) {
|
|
if (utxos[i].confirmations) {
|
|
trx.addinput(utxos[i].txid, utxos[i].vout, utxos[i].scriptPubKey);
|
|
utxoAmt += utxos[i].amount;
|
|
}
|
|
}
|
|
if (utxoAmt < sendAmt)
|
|
return reject("Insufficient balance:" + floID);
|
|
let change = (utxoAmt - sendAmt);
|
|
if (change > 0)
|
|
trx.addoutput(floID, change);
|
|
}
|
|
for (let floID in receivers)
|
|
trx.addoutput(floID, receivers[floID]);
|
|
trx.addflodata(floData.replace(/\n/g, ' '));
|
|
for (let floID in senders)
|
|
trx.sign(senders[floID].wif, 1);
|
|
var signedTxHash = trx.serialize();
|
|
broadcastTx(signedTxHash)
|
|
.then(txid => resolve(txid))
|
|
.catch(error => reject(error))
|
|
}).catch(error => reject(error))
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
//Create a multisig transaction
|
|
const createMultisigTx = function (redeemScript, receivers, amounts, floData = '', strict_utxo = true) {
|
|
return new Promise((resolve, reject) => {
|
|
var multisig = floCrypto.decodeRedeemScript(redeemScript);
|
|
|
|
//validate multisig script and flodata
|
|
if (!multisig)
|
|
return reject(`Invalid redeemScript`);
|
|
var senderAddr = multisig.address;
|
|
if (!floCrypto.validateFloID(senderAddr))
|
|
return reject(`Invalid multisig : ${senderAddr}`);
|
|
else if (!floCrypto.validateASCII(floData))
|
|
return reject("Invalid FLO_Data: only printable ASCII characters are allowed");
|
|
//validate receiver addresses
|
|
if (!Array.isArray(receivers))
|
|
receivers = [receivers];
|
|
for (let r of receivers)
|
|
if (!floCrypto.validateFloID(r))
|
|
return reject(`Invalid address : ${r}`);
|
|
//validate amounts
|
|
if (!Array.isArray(amounts))
|
|
amounts = [amounts];
|
|
if (amounts.length != receivers.length)
|
|
return reject("Receivers and amounts have different length");
|
|
var sendAmt = 0;
|
|
for (let a of amounts) {
|
|
if (typeof a !== 'number' || a <= 0)
|
|
return reject(`Invalid amount : ${a}`);
|
|
sendAmt += a;
|
|
}
|
|
|
|
getBalance(senderAddr).then(balance => {
|
|
var fee = DEFAULT.fee;
|
|
if (balance < sendAmt + fee)
|
|
return reject("Insufficient FLO balance!");
|
|
getUTXOs(senderAddr).then(utxos => {
|
|
//form/construct the transaction data
|
|
var trx = bitjs.transaction();
|
|
var utxoAmt = 0.0;
|
|
for (var i = utxos.length - 1;
|
|
(i >= 0) && (utxoAmt < sendAmt + fee); i--) {
|
|
//use only utxos with confirmations (strict_utxo mode)
|
|
if (utxos[i].confirmations || !strict_utxo) {
|
|
trx.addinput(utxos[i].txid, utxos[i].vout, redeemScript); //for multisig, script=redeemScript
|
|
utxoAmt += utxos[i].amount;
|
|
};
|
|
}
|
|
if (utxoAmt < sendAmt + fee)
|
|
reject("Insufficient FLO: Some UTXOs are unconfirmed");
|
|
else {
|
|
for (let i in receivers)
|
|
trx.addoutput(receivers[i], amounts[i]);
|
|
var change = utxoAmt - sendAmt - fee;
|
|
if (change > DEFAULT.minChangeAmt)
|
|
trx.addoutput(senderAddr, change);
|
|
trx.addflodata(floData.replace(/\n/g, ' '));
|
|
resolve(trx);
|
|
}
|
|
}).catch(error => reject(error))
|
|
}).catch(error => reject(error))
|
|
});
|
|
}
|
|
|
|
//Same as above, but explict call should return serialized tx-hex
|
|
floBlockchainAPI.createMultisigTx = function (redeemScript, receivers, amounts, floData = '', strict_utxo = true) {
|
|
return new Promise((resolve, reject) => {
|
|
createMultisigTx(redeemScript, receivers, amounts, floData, strict_utxo)
|
|
.then(trx => resolve(trx.serialize()))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
//Create and send multisig transaction
|
|
const sendMultisigTx = floBlockchainAPI.sendMultisigTx = function (redeemScript, privateKeys, receivers, amounts, floData = '', strict_utxo = true) {
|
|
return new Promise((resolve, reject) => {
|
|
var multisig = floCrypto.decodeRedeemScript(redeemScript);
|
|
if (!multisig)
|
|
return reject(`Invalid redeemScript`);
|
|
if (privateKeys.length < multisig.required)
|
|
return reject(`Insufficient privateKeys (required ${multisig.required})`);
|
|
for (let pk of privateKeys) {
|
|
var flag = false;
|
|
for (let pub of multisig.pubkeys)
|
|
if (floCrypto.verifyPrivKey(pk, pub, false))
|
|
flag = true;
|
|
if (!flag)
|
|
return reject(`Invalid Private key`);
|
|
}
|
|
createMultisigTx(redeemScript, receivers, amounts, floData, strict_utxo).then(trx => {
|
|
for (let pk of privateKeys)
|
|
trx.sign(pk, 1);
|
|
var signedTxHash = trx.serialize();
|
|
broadcastTx(signedTxHash)
|
|
.then(txid => resolve(txid))
|
|
.catch(error => reject(error))
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
floBlockchainAPI.writeMultisigData = function (redeemScript, data, privatekeys, receiverAddr = DEFAULT.receiverID, options = {}) {
|
|
let strict_utxo = options.strict_utxo === false ? false : true,
|
|
sendAmt = isNaN(options.sendAmt) ? DEFAULT.sendAmt : options.sendAmt;
|
|
return new Promise((resolve, reject) => {
|
|
if (!floCrypto.validateFloID(receiverAddr))
|
|
return reject(`Invalid receiver: ${receiverAddr}`);
|
|
sendMultisigTx(redeemScript, privatekeys, receiverAddr, sendAmt, data, strict_utxo)
|
|
.then(txid => resolve(txid))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
function deserializeTx(tx) {
|
|
if (typeof tx === 'string' || Array.isArray(tx)) {
|
|
try {
|
|
tx = bitjs.transaction(tx);
|
|
} catch {
|
|
throw "Invalid transaction hex";
|
|
}
|
|
} else if (typeof tx !== 'object' || typeof tx.sign !== 'function')
|
|
throw "Invalid transaction object";
|
|
return tx;
|
|
}
|
|
|
|
floBlockchainAPI.signTx = function (tx, privateKey, sighashtype = 1) {
|
|
if (!floCrypto.getFloID(privateKey))
|
|
throw "Invalid Private key";
|
|
//deserialize if needed
|
|
tx = deserializeTx(tx);
|
|
var signedTxHex = tx.sign(privateKey, sighashtype);
|
|
return signedTxHex;
|
|
}
|
|
|
|
const checkSigned = floBlockchainAPI.checkSigned = function (tx, bool = true) {
|
|
tx = deserializeTx(tx);
|
|
let n = [];
|
|
for (let i = 0; i < tx.inputs.length; i++) {
|
|
var s = tx.scriptDecode(i);
|
|
if (s['type'] === 'scriptpubkey')
|
|
n.push(s.signed);
|
|
else if (s['type'] === 'multisig') {
|
|
var rs = tx.decodeRedeemScript(s['rs']);
|
|
let x = {
|
|
s: 0,
|
|
r: rs['required'],
|
|
t: rs['pubkeys'].length
|
|
};
|
|
//check input script for signatures
|
|
var script = Array.from(tx.inputs[i].script);
|
|
if (script[0] == 0) { //script with signatures
|
|
script = tx.parseScript(script);
|
|
for (var k = 0; k < script.length; k++)
|
|
if (Array.isArray(script[k]) && script[k][0] == 48) //0x30 DERSequence
|
|
x.s++;
|
|
}
|
|
//validate counts
|
|
if (x.r > x.t)
|
|
throw "signaturesRequired is more than publicKeys";
|
|
else if (x.s < x.r)
|
|
n.push(x);
|
|
else
|
|
n.push(true);
|
|
}
|
|
}
|
|
return bool ? !(n.filter(x => x !== true).length) : n;
|
|
}
|
|
|
|
floBlockchainAPI.checkIfSameTx = function (tx1, tx2) {
|
|
tx1 = deserializeTx(tx1);
|
|
tx2 = deserializeTx(tx2);
|
|
//compare input and output length
|
|
if (tx1.inputs.length !== tx2.inputs.length || tx1.outputs.length !== tx2.outputs.length)
|
|
return false;
|
|
//compare flodata
|
|
if (tx1.floData !== tx2.floData)
|
|
return false
|
|
//compare inputs
|
|
for (let i = 0; i < tx1.inputs.length; i++)
|
|
if (tx1.inputs[i].outpoint.hash !== tx2.inputs[i].outpoint.hash || tx1.inputs[i].outpoint.index !== tx2.inputs[i].outpoint.index)
|
|
return false;
|
|
//compare outputs
|
|
for (let i = 0; i < tx1.outputs.length; i++)
|
|
if (tx1.outputs[i].value !== tx2.outputs[i].value || Crypto.util.bytesToHex(tx1.outputs[i].script) !== Crypto.util.bytesToHex(tx2.outputs[i].script))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
floBlockchainAPI.transactionID = function (tx) {
|
|
tx = deserializeTx(tx);
|
|
let clone = bitjs.clone(tx);
|
|
let raw_bytes = Crypto.util.hexToBytes(clone.serialize());
|
|
let txid = Crypto.SHA256(Crypto.SHA256(raw_bytes, { asBytes: true }), { asBytes: true }).reverse();
|
|
return Crypto.util.bytesToHex(txid);
|
|
}
|
|
|
|
const getTxOutput = (txid, i) => new Promise((resolve, reject) => {
|
|
promisedAPI(`api/tx/${txid}`)
|
|
.then(result => resolve(result.vout[i]))
|
|
.catch(error => reject(error))
|
|
});
|
|
|
|
function getOutputAddress(outscript) {
|
|
var bytes, version;
|
|
switch (outscript[0]) {
|
|
case 118: //legacy
|
|
bytes = outscript.slice(3, outscript.length - 2);
|
|
version = bitjs.pub;
|
|
break
|
|
case 169: //multisig
|
|
bytes = outscript.slice(2, outscript.length - 1);
|
|
version = bitjs.multisig;
|
|
break;
|
|
default: return; //unknown
|
|
}
|
|
bytes.unshift(version);
|
|
var hash = Crypto.SHA256(Crypto.SHA256(bytes, { asBytes: true }), { asBytes: true });
|
|
var checksum = hash.slice(0, 4);
|
|
return bitjs.Base58.encode(bytes.concat(checksum));
|
|
}
|
|
|
|
floBlockchainAPI.parseTransaction = function (tx) {
|
|
return new Promise((resolve, reject) => {
|
|
tx = deserializeTx(tx);
|
|
let result = {};
|
|
let promises = [];
|
|
//Parse Inputs
|
|
for (let i = 0; i < tx.inputs.length; i++)
|
|
promises.push(getTxOutput(tx.inputs[i].outpoint.hash, tx.inputs[i].outpoint.index));
|
|
Promise.all(promises).then(inputs => {
|
|
result.inputs = inputs.map(inp => Object({
|
|
address: inp.scriptPubKey.addresses[0],
|
|
value: parseFloat(inp.value)
|
|
}));
|
|
let signed = checkSigned(tx, false);
|
|
result.inputs.forEach((inp, i) => inp.signed = signed[i]);
|
|
//Parse Outputs
|
|
result.outputs = tx.outputs.map(out => Object({
|
|
address: getOutputAddress(out.script),
|
|
value: util.Sat_to_FLO(out.value)
|
|
}))
|
|
//Parse Totals
|
|
result.total_input = parseFloat(result.inputs.reduce((a, inp) => a += inp.value, 0).toFixed(8));
|
|
result.total_output = parseFloat(result.outputs.reduce((a, out) => a += out.value, 0).toFixed(8));
|
|
result.fee = parseFloat((result.total_input - result.total_output).toFixed(8));
|
|
result.floData = tx.floData;
|
|
resolve(result);
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
//Broadcast signed Tx in blockchain using API
|
|
const broadcastTx = floBlockchainAPI.broadcastTx = function (signedTxHash) {
|
|
return new Promise((resolve, reject) => {
|
|
if (signedTxHash.length < 1)
|
|
return reject("Empty Transaction Data");
|
|
|
|
promisedAPI('/api/sendtx/' + signedTxHash)
|
|
.then(response => resolve(response["result"]))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
const getTx = floBlockchainAPI.getTx = function (txid) {
|
|
return new Promise((resolve, reject) => {
|
|
promisedAPI(`api/tx/${txid}`)
|
|
.then(response => resolve(response))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
/**Wait for the given txid to get confirmation in blockchain
|
|
* @param {string} txid of the transaction to wait for
|
|
* @param {int} max_retry: maximum number of retries before exiting wait. negative number = Infinite retries (DEFAULT: -1 ie, infinite retries)
|
|
* @param {Array} retry_timeout: time (seconds) between retries (DEFAULT: 20 seconds)
|
|
* @return {Promise} resolves when tx gets confirmation
|
|
*/
|
|
const waitForConfirmation = floBlockchainAPI.waitForConfirmation = function (txid, max_retry = -1, retry_timeout = 20) {
|
|
return new Promise((resolve, reject) => {
|
|
setTimeout(function () {
|
|
getTx(txid).then(tx => {
|
|
if (!tx)
|
|
return reject("Transaction not found");
|
|
if (tx.confirmations)
|
|
return resolve(tx);
|
|
else if (max_retry === 0) //no more retries
|
|
return reject("Waiting timeout: tx still not confirmed");
|
|
else {
|
|
max_retry = max_retry < 0 ? -1 : max_retry - 1; //decrease retry count (unless infinite retries)
|
|
waitForConfirmation(txid, max_retry, retry_timeout)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error))
|
|
}
|
|
}).catch(error => reject(error))
|
|
}, retry_timeout * 1000)
|
|
})
|
|
}
|
|
|
|
//Read Txs of Address
|
|
const readTxs = floBlockchainAPI.readTxs = function (addr, options = {}) {
|
|
return new Promise((resolve, reject) => {
|
|
//API options
|
|
let query_params = { details: 'txs' };
|
|
//page options
|
|
if (!isUndefined(options.page) && Number.isInteger(options.page))
|
|
query_params.page = options.page;
|
|
if (!isUndefined(options.pageSize) && Number.isInteger(options.pageSize))
|
|
query_params.pageSize = options.pageSize;
|
|
//only confirmed tx
|
|
if (options.confirmed) //Default is false in server, so only add confirmed filter if confirmed has a true value
|
|
query_params.confirmed = true;
|
|
|
|
promisedAPI(`api/address/${addr}`, query_params).then(response => {
|
|
if (!Array.isArray(response.txs)) //set empty array if address doesnt have any tx
|
|
response.txs = [];
|
|
resolve(response)
|
|
}).catch(error => reject(error))
|
|
});
|
|
}
|
|
|
|
//backward support (floBlockchainAPI < v2.5.6)
|
|
function readAllTxs_oldSupport(addr, options, ignoreOld = 0, cacheTotal = 0) {
|
|
return new Promise((resolve, reject) => {
|
|
readTxs(addr, options).then(response => {
|
|
cacheTotal += response.txs.length;
|
|
let n_remaining = response.txApperances - cacheTotal
|
|
if (n_remaining < ignoreOld) { // must remove tx that would have been fetch during prev call
|
|
let n_remove = ignoreOld - n_remaining;
|
|
resolve(response.txs.slice(0, -n_remove));
|
|
} else if (response.page == response.totalPages) //last page reached
|
|
resolve(response.txs);
|
|
else {
|
|
options.page = response.page + 1;
|
|
readAllTxs_oldSupport(addr, options, ignoreOld, cacheTotal)
|
|
.then(result => resolve(response.txs.concat(result)))
|
|
.catch(error => reject(error))
|
|
}
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
function readAllTxs_new(addr, options, lastItem) {
|
|
return new Promise((resolve, reject) => {
|
|
readTxs(addr, options).then(response => {
|
|
let i = response.txs.findIndex(t => t.txid === lastItem);
|
|
if (i != -1) //found lastItem
|
|
resolve(response.txs.slice(0, i))
|
|
else if (response.page == response.totalPages) //last page reached
|
|
resolve(response.txs);
|
|
else {
|
|
options.page = response.page + 1;
|
|
readAllTxs_new(addr, options, lastItem)
|
|
.then(result => resolve(response.txs.concat(result)))
|
|
.catch(error => reject(error))
|
|
}
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
//Read All Txs of Address (newest first)
|
|
const readAllTxs = floBlockchainAPI.readAllTxs = function (addr, options = {}) {
|
|
return new Promise((resolve, reject) => {
|
|
if (Number.isInteger(options.ignoreOld)) //backward support: data from floBlockchainAPI < v2.5.6
|
|
readAllTxs_oldSupport(addr, options, options.ignoreOld).then(txs => {
|
|
let last_tx = txs.find(t => t.confirmations > 0);
|
|
let new_lastItem = last_tx ? last_tx.txid : options.ignoreOld;
|
|
resolve({
|
|
lastItem: new_lastItem,
|
|
items: txs
|
|
})
|
|
|
|
}).catch(error => reject(error))
|
|
else //New format for floBlockchainAPI >= v2.5.6
|
|
readAllTxs_new(addr, options, options.after).then(txs => {
|
|
let last_tx = txs.find(t => t.confirmations > 0);
|
|
let new_lastItem = last_tx ? last_tx.txid : options.after;
|
|
resolve({
|
|
lastItem: new_lastItem,
|
|
items: txs
|
|
})
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
/*Read flo Data from txs of given Address
|
|
options can be used to filter data
|
|
after : query after the given txid
|
|
confirmed : query only confirmed tx or not (options same as readAllTx, DEFAULT=true: only_confirmed_tx)
|
|
ignoreOld : ignore old txs (deprecated: support for backward compatibility only, cannot be used with 'after')
|
|
sentOnly : filters only sent data
|
|
receivedOnly: filters only received data
|
|
pattern : filters data that with JSON pattern
|
|
filter : custom filter funtion for floData (eg . filter: d => {return d[0] == '$'})
|
|
tx : (boolean) resolve tx data or not (resolves an Array of Object with tx details)
|
|
sender : flo-id(s) of sender
|
|
receiver : flo-id(s) of receiver
|
|
*/
|
|
floBlockchainAPI.readData = function (addr, options = {}) {
|
|
return new Promise((resolve, reject) => {
|
|
|
|
//fetch options
|
|
let query_options = {};
|
|
query_options.confirmed = isUndefined(options.confirmed) ? true : options.confirmed; //DEFAULT: ignore unconfirmed tx
|
|
|
|
if (!isUndefined(options.after))
|
|
query_options.after = options.after;
|
|
else if (!isUndefined(options.ignoreOld))
|
|
query_options.ignoreOld = options.ignoreOld;
|
|
|
|
readAllTxs(addr, query_options).then(response => {
|
|
|
|
if (typeof options.senders === "string") options.senders = [options.senders];
|
|
if (typeof options.receivers === "string") options.receivers = [options.receivers];
|
|
|
|
//filter the txs based on options
|
|
const filteredData = response.items.filter(tx => {
|
|
|
|
if (!tx.confirmations) //unconfirmed transactions: this should not happen as we send mempool=false in API query
|
|
return false;
|
|
|
|
if (options.sentOnly && !tx.vin.some(vin => vin.addresses[0] === addr))
|
|
return false;
|
|
else if (Array.isArray(options.senders) && !tx.vin.some(vin => options.senders.includes(vin.addresses[0])))
|
|
return false;
|
|
|
|
if (options.receivedOnly && !tx.vout.some(vout => vout.scriptPubKey.addresses[0] === addr))
|
|
return false;
|
|
else if (Array.isArray(options.receivers) && !tx.vout.some(vout => options.receivers.includes(vout.scriptPubKey.addresses[0])))
|
|
return false;
|
|
|
|
if (options.pattern) {
|
|
try {
|
|
let jsonContent = JSON.parse(tx.floData);
|
|
if (!Object.keys(jsonContent).includes(options.pattern))
|
|
return false;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (options.filter && !options.filter(tx.floData))
|
|
return false;
|
|
|
|
return true;
|
|
}).map(tx => options.tx ? {
|
|
txid: tx.txid,
|
|
time: tx.time,
|
|
blockheight: tx.blockheight,
|
|
senders: new Set(tx.vin.map(v => v.addresses[0])),
|
|
receivers: new Set(tx.vout.map(v => v.scriptPubKey.addresses[0])),
|
|
data: tx.floData
|
|
} : tx.floData);
|
|
|
|
const result = { lastItem: response.lastItem };
|
|
if (options.tx)
|
|
result.items = filteredData;
|
|
else
|
|
result.data = filteredData
|
|
resolve(result);
|
|
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
/*Get the latest flo Data that match the caseFn from txs of given Address
|
|
caseFn: (function) flodata => return bool value
|
|
options can be used to filter data
|
|
after : query after the given txid
|
|
confirmed : query only confirmed tx or not (options same as readAllTx, DEFAULT=true: only_confirmed_tx)
|
|
sentOnly : filters only sent data
|
|
receivedOnly: filters only received data
|
|
tx : (boolean) resolve tx data or not (resolves an Array of Object with tx details)
|
|
sender : flo-id(s) of sender
|
|
receiver : flo-id(s) of receiver
|
|
*/
|
|
const getLatestData = floBlockchainAPI.getLatestData = function (addr, caseFn, options = {}) {
|
|
return new Promise((resolve, reject) => {
|
|
//fetch options
|
|
let query_options = {};
|
|
query_options.confirmed = isUndefined(options.confirmed) ? true : options.confirmed; //DEFAULT: confirmed tx only
|
|
if (!isUndefined(options.page))
|
|
query_options.page = options.page;
|
|
//if (!isUndefined(options.after)) query_options.after = options.after;
|
|
|
|
let new_lastItem;
|
|
readTxs(addr, query_options).then(response => {
|
|
|
|
//lastItem confirmed tx checked
|
|
if (!new_lastItem) {
|
|
let last_tx = response.items.find(t => t.confirmations > 0);
|
|
if (last_tx)
|
|
new_lastItem = last_tx.txid;
|
|
}
|
|
|
|
if (typeof options.senders === "string") options.senders = [options.senders];
|
|
if (typeof options.receivers === "string") options.receivers = [options.receivers];
|
|
|
|
//check if `after` txid is in the response
|
|
let i_after = response.txs.findIndex(t => t.txid === options.after);
|
|
if (i_after != -1) //found lastItem, hence remove it and all txs before that
|
|
response.items.splice(i_after);
|
|
|
|
var item = response.items.find(tx => {
|
|
if (!tx.confirmations) //unconfirmed transactions: this should not happen as we send mempool=false in API query
|
|
return false;
|
|
|
|
if (options.sentOnly && !tx.vin.some(vin => vin.addresses[0] === addr))
|
|
return false;
|
|
else if (Array.isArray(options.senders) && !tx.vin.some(vin => options.senders.includes(vin.addresses[0])))
|
|
return false;
|
|
|
|
if (options.receivedOnly && !tx.vout.some(vout => vout.scriptPubKey.addresses[0] === addr))
|
|
return false;
|
|
else if (Array.isArray(options.receivers) && !tx.vout.some(vout => options.receivers.includes(vout.scriptPubKey.addresses[0])))
|
|
return false;
|
|
|
|
return caseFn(tx.floData) ? true : false; //return only bool for find fn
|
|
});
|
|
|
|
//if item found, then resolve the result
|
|
if (!isUndefined(item)) {
|
|
const result = { lastItem: new_lastItem || item.txid };
|
|
if (options.tx) {
|
|
result.item = {
|
|
txid: item.txid,
|
|
time: item.time,
|
|
blockheight: item.blockheight,
|
|
senders: new Set(item.vin.map(v => v.addresses[0])),
|
|
receivers: new Set(item.vout.map(v => v.scriptPubKey.addresses[0])),
|
|
data: item.floData
|
|
}
|
|
} else
|
|
result.data = item.floData;
|
|
return resolve(result);
|
|
}
|
|
|
|
if (response.page == response.totalPages || i_after != -1) //reached last page to check
|
|
resolve({ lastItem: new_lastItem || options.after }); //no data match the caseFn, resolve just the lastItem
|
|
|
|
//else if address needs chain query
|
|
else {
|
|
options.page = response.page + 1;
|
|
getLatestData(addr, caseFn, options)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error))
|
|
}
|
|
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
})('object' === typeof module ? module.exports : window.floBlockchainAPI = {});
|