Last active
July 28, 2016 16:31
-
-
Save nikhiljha/e5848853604bf54ec085dc8fe58b19f8 to your computer and use it in GitHub Desktop.
NEM Signing/API Examples
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
blank |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
define([ | |
'definitions', | |
'jquery', | |
'utils/CryptoHelpers', | |
'utils/KeyPair', | |
'utils/TransactionType', | |
'utils/convert', | |
'services/SessionData' | |
], function(angular, $, CryptoHelpers, KeyPair, TransactionType, convert){ | |
var mod = angular.module('walletApp.services'); | |
mod.factory('Transactions', ['$http', 'sessionData', | |
function TransactionsFactory($http, sessionData) { | |
var o = { | |
}; | |
var NEM_EPOCH = Date.UTC(2015, 2, 29, 0, 6, 25, 0); | |
var CURRENT_NETWORK_ID = sessionData.getNetworkId(); | |
var _dummy = document.createElement('a'); | |
_dummy.href = sessionData.getNode().uri; | |
var CURRENT_HOSTNAME = _dummy.hostname; | |
var CURRENT_NETWORK_VERSION = function(val) { | |
if (CURRENT_NETWORK_ID === 104) { | |
return 0x68000000 | val; | |
} else if (CURRENT_NETWORK_ID === -104) { | |
return 0x98000000 | val; | |
} | |
return 0x60000000 | val; | |
}; | |
function CREATE_DATA(txtype, senderPublicKey, timeStamp, due, version) | |
{ | |
return { | |
'type': txtype, | |
'version': version || CURRENT_NETWORK_VERSION(1), | |
'signer': senderPublicKey, | |
'timeStamp': timeStamp, | |
'deadline': timeStamp + due * 60 | |
}; | |
} | |
function CALC_MIN_FEE(numNem) { | |
return Math.ceil(Math.max(10 - numNem, 2, Math.floor(Math.atan(numNem / 150000.0) * 3 * 33))); | |
} | |
o.getTimeStamp = function() { | |
return Math.floor((Date.now() / 1000) - (NEM_EPOCH / 1000)); | |
}; | |
o._constructTransfer = function(senderPublicKey, recipientCompressedKey, amount, message, due, mosaics, mosaicsFee) { | |
var timeStamp = o.getTimeStamp(); | |
var version = mosaics ? CURRENT_NETWORK_VERSION(2) : CURRENT_NETWORK_VERSION(1); | |
var data = CREATE_DATA(0x101, senderPublicKey, timeStamp, due, version); | |
var msgFee = message.payload.length ? Math.max(1, Math.floor(message.payload.length / 2 / 16)) * 2 : 0; | |
var fee = mosaics ? mosaicsFee : CALC_MIN_FEE(amount / 1000000); | |
var totalFee = (msgFee + fee) * 1000000; | |
var custom = { | |
'recipient': recipientCompressedKey.toUpperCase().replace(/-/g, ''), | |
'amount': amount, | |
'fee': totalFee, | |
'message': message, | |
'mosaics': mosaics | |
}; | |
var entity = $.extend(data, custom); | |
//console.log(entity); | |
return entity; | |
}; | |
//actualSender, namespaceParent, namespaceName | |
o._constructNamespace = function(senderPublicKey, rentalFeeSink, rentalFee, namespaceParent, namespaceName, due) { | |
var timeStamp = o.getTimeStamp(); | |
var version = CURRENT_NETWORK_VERSION(1); | |
var data = CREATE_DATA(0x2001, senderPublicKey, timeStamp, due, version); | |
var fee = 2 * 3 * 18; | |
var totalFee = (fee) * 1000000; | |
var custom = { | |
'rentalFeeSink': rentalFeeSink.toUpperCase().replace(/-/g, ''), | |
'rentalFee': rentalFee, | |
'parent': namespaceParent, | |
'newPart': namespaceName, | |
'fee': totalFee | |
}; | |
var entity = $.extend(data, custom); | |
//console.log(entity); | |
return entity; | |
}; | |
o._constructMosaicDefinition = function(senderPublicKey, rentalFeeSink, rentalFee, namespaceParent, mosaicName, mosaicDescription, mosaicProperties, levy, due) { | |
var timeStamp = o.getTimeStamp(); | |
var version = CURRENT_NETWORK_VERSION(1); | |
var data = CREATE_DATA(0x4001, senderPublicKey, timeStamp, due, version); | |
var fee = 2 * 3 * 18; | |
var totalFee = (fee) * 1000000; | |
var levyData = levy ? { | |
'type': levy.feeType, | |
'recipient': levy.address.toUpperCase().replace(/-/g, ''), | |
'mosaicId': levy.mosaic, | |
'fee': levy.fee, | |
} : null; | |
var custom = { | |
'creationFeeSink': rentalFeeSink.replace(/-/g, ''), | |
'creationFee': rentalFee, | |
'mosaicDefinition':{ | |
'creator': senderPublicKey, | |
'id': { | |
'namespaceId': namespaceParent, | |
'name': mosaicName, | |
}, | |
'description': mosaicDescription, | |
'properties': $.map(mosaicProperties, function(v,k){ | |
return {'name':k, 'value':v.toString()}; | |
}), | |
'levy': levyData | |
}, | |
'fee': totalFee | |
}; | |
var entity = $.extend(data, custom); | |
//console.log(entity); | |
return entity; | |
}; | |
o._constructMosaicSupply = function(senderPublicKey, mosaicId, supplyType, delta, due) { | |
var timeStamp = o.getTimeStamp(); | |
var version = CURRENT_NETWORK_VERSION(1); | |
var data = CREATE_DATA(0x4002, senderPublicKey, timeStamp, due, version); | |
var fee = 2 * 3 * 18; | |
var totalFee = (fee) * 1000000; | |
var custom = { | |
'mosaicId': mosaicId, | |
'supplyType': supplyType, | |
'delta': delta, | |
'fee': totalFee | |
}; | |
var entity = $.extend(data, custom); | |
//console.log(entity); | |
return entity; | |
}; | |
o._constructSignature = function(senderPublicKey, otherAccount, otherHash, due) { | |
var timeStamp = o.getTimeStamp(); | |
var version = CURRENT_NETWORK_VERSION(1); | |
var data = CREATE_DATA(0x1002, senderPublicKey, timeStamp, due, version); | |
var totalFee = (2 * 3) * 1000000; | |
var custom = { | |
'otherHash': { 'data': otherHash }, | |
'otherAccount': otherAccount, | |
'fee': totalFee, | |
}; | |
var entity = $.extend(data, custom); | |
return entity; | |
}; | |
o._multisigWrapper = function(senderPublicKey, innerEntity, due) { | |
var timeStamp = o.getTimeStamp(); | |
var version = CURRENT_NETWORK_VERSION(1); | |
var data = CREATE_DATA(0x1004, senderPublicKey, timeStamp, due, version); | |
var custom = { | |
'fee': 18000000, | |
'otherTrans': innerEntity | |
}; | |
var entity = $.extend(data, custom); | |
//console.log("_multisigWrapper: ", entity); | |
return entity; | |
}; | |
/** | |
* NOTE, related to serialization: Unfortunately we need to create few objects | |
* and do a bit of copying, as Uint32Array does not allow random offsets | |
*/ | |
/* safe string - each char is 8 bit */ | |
o._serializeSafeString = function(str) { | |
var r = new ArrayBuffer(132); | |
var d = new Uint32Array(r); | |
var b = new Uint8Array(r); | |
var e = 4; | |
if (str === null) { | |
d[0] = 0xffffffff; | |
} else { | |
d[0] = str.length; | |
for (var j = 0; j < str.length; ++j) { | |
b[e++] = str.charCodeAt(j); | |
} | |
} | |
return new Uint8Array(r, 0, e); | |
}; | |
o._serializeUaString = function(str) { | |
var r = new ArrayBuffer(516); | |
var d = new Uint32Array(r); | |
var b = new Uint8Array(r); | |
var e = 4; | |
if (str === null) { | |
d[0] = 0xffffffff; | |
} else { | |
d[0] = str.length; | |
for (var j = 0; j < str.length; ++j) { | |
b[e++] = str[j]; | |
} | |
} | |
return new Uint8Array(r, 0, e); | |
}; | |
o._serializeLong = function(value) { | |
var r = new ArrayBuffer(8); | |
var d = new Uint32Array(r); | |
d[0] = value; | |
d[1] = Math.floor((value / 0x100000000)); | |
return new Uint8Array(r, 0, 8); | |
}; | |
o._serializeMosaicId = function(mosaicId) { | |
var r = new ArrayBuffer(264); | |
var serializedNamespaceId = o._serializeSafeString(mosaicId.namespaceId); | |
var serializedName = o._serializeSafeString(mosaicId.name); | |
var b = new Uint8Array(r); | |
var d = new Uint32Array(r); | |
d[0] = serializedNamespaceId.length + serializedName.length; | |
var e = 4; | |
for (var j=0; j<serializedNamespaceId.length; ++j) { | |
b[e++] = serializedNamespaceId[j]; | |
} | |
for (var j=0; j<serializedName.length; ++j) { | |
b[e++] = serializedName[j]; | |
} | |
return new Uint8Array(r, 0, e); | |
}; | |
o._serializeMosaicAndQuantity = function(mosaic) { | |
var r = new ArrayBuffer(4 + 264 + 8); | |
var serializedMosaicId = o._serializeMosaicId(mosaic.mosaicId); | |
var serializedQuantity = o._serializeLong(mosaic.quantity); | |
//console.log(convert.ua2hex(serializedQuantity), serializedMosaicId, serializedQuantity); | |
var b = new Uint8Array(r); | |
var d = new Uint32Array(r); | |
d[0] = serializedMosaicId.length + serializedQuantity.length; | |
var e = 4; | |
for (var j=0; j<serializedMosaicId.length; ++j) { | |
b[e++] = serializedMosaicId[j]; | |
} | |
for (var j=0; j<serializedQuantity.length; ++j) { | |
b[e++] = serializedQuantity[j]; | |
} | |
return new Uint8Array(r, 0, e); | |
}; | |
o._serializeMosaics = function(entity) { | |
var r = new ArrayBuffer(276*10 + 4); | |
var d = new Uint32Array(r); | |
var b = new Uint8Array(r); | |
var i = 0; | |
var e = 0; | |
d[i++] = entity.length; | |
e += 4; | |
var temporary = []; | |
for (var j=0; j<entity.length; ++j) { | |
temporary.push({'entity':entity[j], 'value':mosaicIdToName(entity[j].mosaicId) + " : " + entity[j].quantity}) | |
} | |
temporary.sort(function(a, b) {return a.value < b.value ? -1 : a.value > b.value;}); | |
for (var j=0; j<temporary.length; ++j) { | |
var entity = temporary[j].entity; | |
var serializedMosaic = o._serializeMosaicAndQuantity(entity); | |
for (var k=0; k<serializedMosaic.length; ++k) { | |
b[e++] = serializedMosaic[k]; | |
} | |
} | |
return new Uint8Array(r, 0, e); | |
}; | |
o._serializeProperty = function(entity) { | |
var r = new ArrayBuffer(1024); | |
var d = new Uint32Array(r); | |
var b = new Uint8Array(r); | |
var serializedName = o._serializeSafeString(entity['name']); | |
var serializedValue = o._serializeSafeString(entity['value']); | |
d[0] = serializedName.length + serializedValue.length; | |
var e = 4; | |
for (var j = 0; j<serializedName.length; ++j) { b[e++] = serializedName[j]; } | |
for (var j = 0; j<serializedValue.length; ++j) { b[e++] = serializedValue[j]; } | |
return new Uint8Array(r, 0, e); | |
}; | |
o._serializeProperties = function(entity) { | |
var r = new ArrayBuffer(1024); | |
var d = new Uint32Array(r); | |
var b = new Uint8Array(r); | |
var i = 0; | |
var e = 0; | |
d[i++] = entity.length; | |
e += 4; | |
var temporary = entity; | |
var temporary = []; | |
for (var j=0; j<entity.length; ++j) { | |
temporary.push(entity[j]); | |
} | |
var helper = {'divisibility':1, 'initialSupply':2, 'supplyMutable':3, 'transferable':4}; | |
temporary.sort(function(a, b) {return helper[a.name] < helper[b.name] ? -1 : helper[a.name] > helper[b.name];}); | |
for (var j=0; j<temporary.length; ++j) { | |
var entity = temporary[j]; | |
var serializedProperty = o._serializeProperty(entity); | |
for (var k=0; k<serializedProperty.length; ++k) { | |
b[e++] = serializedProperty[k]; | |
} | |
} | |
return new Uint8Array(r, 0, e); | |
}; | |
o._serializeLevy = function(entity) { | |
var r = new ArrayBuffer(1024); | |
var d = new Uint32Array(r); | |
if (entity === null) | |
{ | |
d[0] = 0; | |
return new Uint8Array(r, 0, 4); | |
} | |
var b = new Uint8Array(r); | |
d[1] = entity['type']; | |
var e = 8; | |
var temp = o._serializeSafeString(entity['recipient']); | |
for (var j = 0; j<temp.length; ++j) { b[e++] = temp[j]; } | |
var serializedMosaicId = o._serializeMosaicId(entity['mosaicId']); | |
for (var j=0; j<serializedMosaicId.length; ++j) { | |
b[e++] = serializedMosaicId[j]; | |
} | |
var serializedFee = o._serializeLong(entity['fee']); | |
for (var j=0; j<serializedFee.length; ++j) { | |
b[e++] = serializedFee[j]; | |
} | |
d[0] = 4 + temp.length + serializedMosaicId.length + 8; | |
return new Uint8Array(r, 0, e); | |
}; | |
o._serializeMosaicDefinition = function(entity) { | |
var r = new ArrayBuffer(40 + 264 + 516 + 1024 + 1024); | |
var d = new Uint32Array(r); | |
var b = new Uint8Array(r); | |
var temp = convert.hex2ua(entity['creator']); | |
d[0] = temp.length; | |
var e = 4; | |
for (var j = 0; j<temp.length; ++j) { b[e++] = temp[j]; } | |
var serializedMosaicId = o._serializeMosaicId(entity.id); | |
for (var j=0; j<serializedMosaicId.length; ++j) { | |
b[e++] = serializedMosaicId[j]; | |
} | |
var utf8ToUa = convert.hex2ua(convert.utf8ToHex(entity['description'])); | |
var temp = o._serializeUaString(utf8ToUa); | |
for (var j=0; j<temp.length; ++j) { | |
b[e++] = temp[j]; | |
} | |
var temp = o._serializeProperties(entity['properties']); | |
for (var j=0; j<temp.length; ++j) { | |
b[e++] = temp[j]; | |
} | |
var levy = o._serializeLevy(entity['levy']); | |
for (var j=0; j<levy.length; ++j) { | |
b[e++] = levy[j]; | |
} | |
return new Uint8Array(r, 0, e); | |
}; | |
o.serializeTransaction = function(entity) { | |
var r = new ArrayBuffer(512 + 2764); | |
var d = new Uint32Array(r); | |
var b = new Uint8Array(r); | |
d[0] = entity['type']; | |
d[1] = entity['version']; | |
d[2] = entity['timeStamp']; | |
var temp = convert.hex2ua(entity['signer']); | |
d[3] = temp.length; | |
var e = 16; | |
for (var j = 0; j<temp.length; ++j) { b[e++] = temp[j]; } | |
// Transaction | |
var i = e / 4; | |
d[i++] = entity['fee']; | |
d[i++] = Math.floor((entity['fee'] / 0x100000000)); | |
d[i++] = entity['deadline']; | |
e += 12; | |
// TransferTransaction | |
if (d[0] === TransactionType.Transfer) { | |
d[i++] = entity['recipient'].length; | |
e += 4; | |
// TODO: check that entity['recipient'].length is always 40 bytes | |
for (var j = 0; j < entity['recipient'].length; ++j) { | |
b[e++] = entity['recipient'].charCodeAt(j); | |
} | |
i = e / 4; | |
d[i++] = entity['amount']; | |
d[i++] = Math.floor((entity['amount'] / 0x100000000)); | |
e += 8; | |
if (entity['message']['type'] === 1 || entity['message']['type'] === 2) { | |
var temp = convert.hex2ua(entity['message']['payload']); | |
if (temp.length === 0) { | |
d[i++] = 0; | |
e += 4; | |
} else { | |
// length of a message object | |
d[i++] = 8 + temp.length; | |
// object itself | |
d[i++] = entity['message']['type']; | |
d[i++] = temp.length; | |
e += 12; | |
for (var j = 0; j<temp.length; ++j) { b[e++] = temp[j]; } | |
} | |
} | |
var entityVersion = d[1] & 0xffffff; | |
if (entityVersion >= 2) { | |
var temp = o._serializeMosaics(entity['mosaics']); | |
for (var j = 0; j<temp.length; ++j) { b[e++] = temp[j]; } | |
} | |
// Provision Namespace transaction | |
} else if (d[0] === TransactionType.ProvisionNamespace) { | |
d[i++] = entity['rentalFeeSink'].length; | |
e += 4; | |
// TODO: check that entity['rentalFeeSink'].length is always 40 bytes | |
for (var j = 0; j < entity['rentalFeeSink'].length; ++j) { | |
b[e++] = entity['rentalFeeSink'].charCodeAt(j); | |
} | |
i = e / 4; | |
d[i++] = entity['rentalFee']; | |
d[i++] = Math.floor((entity['rentalFee'] / 0x100000000)); | |
e += 8; | |
var temp = o._serializeSafeString(entity['newPart']); | |
for (var j = 0; j<temp.length; ++j) { b[e++] = temp[j]; } | |
var temp = o._serializeSafeString(entity['parent']); | |
for (var j = 0; j<temp.length; ++j) { b[e++] = temp[j]; } | |
// Mosaic Definition Creation transaction | |
} else if (d[0] === TransactionType.MosaicDefinition) { | |
var temp = o._serializeMosaicDefinition(entity['mosaicDefinition']); | |
d[i++] = temp.length; | |
e += 4; | |
for (var j = 0; j<temp.length; ++j) { b[e++] = temp[j]; } | |
temp = o._serializeSafeString(entity['creationFeeSink']); | |
for (var j = 0; j<temp.length; ++j) { b[e++] = temp[j]; } | |
temp = o._serializeLong(entity['creationFee']); | |
for (var j = 0; j<temp.length; ++j) { b[e++] = temp[j]; } | |
// Mosaic Supply Change transaction | |
} else if (d[0] === TransactionType.MosaicSupply) { | |
var serializedMosaicId = o._serializeMosaicId(entity['mosaicId']); | |
for (var j=0; j<serializedMosaicId.length; ++j) { | |
b[e++] = serializedMosaicId[j]; | |
} | |
var temp = new ArrayBuffer(4); | |
d = new Uint32Array(temp); | |
d[0] = entity['supplyType']; | |
var serializeSupplyType = new Uint8Array(temp); | |
for (var j=0; j<serializeSupplyType.length; ++j) { | |
b[e++] = serializeSupplyType[j]; | |
} | |
var serializedDelta = o._serializeLong(entity['delta']); | |
for (var j=0; j<serializedDelta.length; ++j) { | |
b[e++] = serializedDelta[j]; | |
} | |
// Signature transaction | |
} else if (d[0] === TransactionType.MultisigSignature) { | |
var temp = convert.hex2ua(entity['otherHash']['data']); | |
// length of a hash object.... | |
d[i++] = 4 + temp.length; | |
// object itself | |
d[i++] = temp.length; | |
e += 8; | |
for (var j = 0; j<temp.length; ++j) { b[e++] = temp[j]; } | |
i = e / 4; | |
temp = entity['otherAccount']; | |
d[i++] = temp.length; | |
e += 4; | |
for (var j = 0; j < temp.length; ++j) { | |
b[e++] = temp.charCodeAt(j); | |
} | |
// Multisig wrapped transaction | |
} else if (d[0] === TransactionType.MultisigTransaction) { | |
var temp = o.serializeTransaction(entity['otherTrans']); | |
d[i++] = temp.length; | |
e += 4; | |
for (var j = 0; j<temp.length; ++j) { b[e++] = temp[j]; } | |
} | |
return new Uint8Array(r, 0, e); | |
}; | |
o.prepareMessage = function prepareMessage(common, tx) { | |
if (tx.encryptMessage) { | |
if (!tx.recipientPubKey || !tx.message || !common.privatekey) { | |
return {'type':0, 'payload':''}; | |
} | |
return {'type':2, 'payload':CryptoHelpers.encode(common.privatekey, tx.recipientPubKey, tx.message.toString())}; | |
} | |
return {'type': 1, 'payload':convert.utf8ToHex(tx.message.toString())} | |
}; | |
o.prepareTransfer = function(common, tx) { | |
//console.log('prepareTransfer', tx); | |
var kp = KeyPair.create(common.privatekey); | |
var actualSender = tx.isMultisig ? tx.multisigAccount.publicKey : kp.publicKey.toString(); | |
var recipientCompressedKey = tx.recipient.toString(); | |
var amount = parseInt(tx.amount * 1000000, 10); | |
var message = o.prepareMessage(common, tx); | |
var due = tx.due; | |
var mosaics = null; | |
var mosaicsFee = null; | |
var entity = o._constructTransfer(actualSender, recipientCompressedKey, amount, message, due, mosaics, mosaicsFee); | |
if (tx.isMultisig) { | |
entity = o._multisigWrapper(kp.publicKey.toString(), entity, due); | |
} | |
return entity; | |
}; | |
function mosaicIdToName(mosaicId) { | |
return mosaicId.namespaceId + ":" + mosaicId.name; | |
} | |
function calcXemEquivalent(multiplier, q, sup, divisibility) { | |
if (sup === 0) { | |
return 0; | |
} | |
// TODO: can this go out of JS (2^54) bounds? (possible BUG) | |
return 8999999999 * q * multiplier / sup / Math.pow(10, divisibility + 6); | |
} | |
o.calculateMosaicsFee = function(multiplier, mosaics, attachedMosaics) { | |
var totalFee = 0; | |
for (var m of attachedMosaics) { | |
// TODO: copied from filters, refactor | |
var mosaicName = mosaicIdToName(m.mosaicId); | |
if (!(mosaicName in mosaics)) { return ['unknown mosaic divisibility', data]; } | |
var mosaicDefinitionMetaDataPair = mosaics[mosaicName]; | |
var divisibilityProperties = $.grep(mosaicDefinitionMetaDataPair.mosaicDefinition.properties, function(w){ return w.name === "divisibility"; }); | |
var divisibility = divisibilityProperties.length === 1 ? ~~(divisibilityProperties[0].value) : 0; | |
//var supply = mosaicDefinitionMetaDataPair.meta.supply; | |
var supply = mosaicDefinitionMetaDataPair.supply; | |
var quantity = m.quantity; | |
var numNem = calcXemEquivalent(multiplier, quantity, supply, divisibility); | |
var fee = CALC_MIN_FEE(numNem); | |
//console.log("CALCULATING FEE for ", m, mosaicDefinitionMetaDataPair, "divisibility", divisibility, "nem equivalent", numNem, "calculated fee", fee); | |
totalFee += fee; | |
} | |
return (totalFee * 5) / 4; | |
}; | |
o.prepareTransferV2 = function(common, mosaicsMetaData, tx) { | |
//console.log('prepareTransferV2', tx); | |
var kp = KeyPair.create(common.privatekey); | |
var actualSender = tx.isMultisig ? tx.multisigAccount.publicKey : kp.publicKey.toString(); | |
var recipientCompressedKey = tx.recipient.toString(); | |
// multiplier | |
var amount = parseInt(tx.multiplier * 1000000, 10); | |
var message = o.prepareMessage(common, tx); | |
var due = tx.due; | |
var mosaics = tx.mosaics; | |
var mosaicsFee = o.calculateMosaicsFee(amount, mosaicsMetaData, mosaics); | |
var entity = o._constructTransfer(actualSender, recipientCompressedKey, amount, message, due, mosaics, mosaicsFee); | |
if (tx.isMultisig) { | |
entity = o._multisigWrapper(kp.publicKey.toString(), entity, due); | |
} | |
return entity; | |
}; | |
o.prepareNamespace = function(common, tx) { | |
var kp = KeyPair.create(common.privatekey); | |
var actualSender = tx.isMultisig ? tx.multisigAccount.publicKey : kp.publicKey.toString(); | |
var rentalFeeSink = tx.rentalFeeSink.toString(); | |
var rentalFee = tx.rentalFee; | |
var namespaceParent = tx.namespaceParent ? tx.namespaceParent.fqn : null; | |
var namespaceName = tx.namespaceName.toString(); | |
var due = tx.due; | |
var entity = o._constructNamespace(actualSender, rentalFeeSink, rentalFee, namespaceParent, namespaceName, due); | |
if (tx.isMultisig) { | |
entity = o._multisigWrapper(kp.publicKey.toString(), entity, due); | |
} | |
return entity; | |
}; | |
o.prepareMosaicDefinition = function(common, tx) { | |
var kp = KeyPair.create(common.privatekey); | |
var actualSender = tx.isMultisig ? tx.multisigAccount.publicKey : kp.publicKey.toString(); | |
var rentalFeeSink = tx.mosaicFeeSink.toString(); | |
var rentalFee = tx.mosaicFee; | |
var namespaceParent = tx.namespaceParent.fqn; | |
var mosaicName = tx.mosaicName.toString(); | |
var mosaicDescription = tx.mosaicDescription.toString(); | |
var mosaicProperties = tx.properties; | |
var levy = tx.levy.mosaic ? tx.levy : null; | |
var due = tx.due; | |
var entity = o._constructMosaicDefinition(actualSender, rentalFeeSink, rentalFee, namespaceParent, mosaicName, mosaicDescription, mosaicProperties, levy, due); | |
if (tx.isMultisig) { | |
entity = o._multisigWrapper(kp.publicKey.toString(), entity, due); | |
} | |
return entity; | |
}; | |
o.prepareMosaicSupply = function(common, tx) { | |
var kp = KeyPair.create(common.privatekey); | |
var actualSender = tx.isMultisig ? tx.multisigAccount.publicKey : kp.publicKey.toString(); | |
var due = tx.due; | |
var entity = o._constructMosaicSupply(actualSender, tx.mosaic, tx.supplyType, tx.delta, due); | |
if (tx.isMultisig) { | |
entity = o._multisigWrapper(kp.publicKey.toString(), entity, due); | |
} | |
return entity; | |
}; | |
o.prepareSignature = function(common, tx, nisPort, cb, failedCb) { | |
var kp = KeyPair.create(fixPrivateKey(common.privatekey)); | |
var actualSender = kp.publicKey.toString(); | |
var otherAccount = tx.multisigAccountAddress.toString(); | |
var otherHash = tx.hash.toString(); | |
var due = tx.due; | |
var entity = o._constructSignature(actualSender, otherAccount, otherHash, due); | |
var result = o.serializeTransaction(entity); | |
var signature = kp.sign(result); | |
var obj = {'data':convert.ua2hex(result), 'signature':signature.toString()}; | |
/* | |
$http.post('http://'+CURRENT_HOSTNAME+':7890/transaction/prepare', entity).then(function (data){ | |
var serializedTx = data.data; | |
var signature = kp.sign(serializedTx.data); | |
var obj = {'data':serializedTx.data, 'signature':signature.toString()}; | |
console.log('nis', obj.data); | |
console.log(' js', convert.ua2hex(result)); | |
}, function(data) { | |
failedCb('prepare', data); | |
}); | |
/*/ | |
return $http.post('http://'+CURRENT_HOSTNAME+':'+nisPort+'/transaction/announce', obj).then(function (data){ | |
cb(data); | |
}, function(data) { | |
failedCb('announce', data); | |
}); | |
//*/ | |
}; | |
function fixPrivateKey(privatekey) { | |
return ("0000000000000000000000000000000000000000000000000000000000000000" + privatekey.replace(/^00/, '')).slice(-64); | |
} | |
o.serializeAndAnnounceTransaction = function(entity, common, tx, nisPort, cb, failedCb) { | |
var kp = KeyPair.create(fixPrivateKey(common.privatekey)); | |
var result = o.serializeTransaction(entity); | |
var signature = kp.sign(result); | |
var obj = {'data':convert.ua2hex(result), 'signature':signature.toString()}; | |
/* leaving this here for testing purposes * | |
$http.post('http://'+CURRENT_HOSTNAME+':7890/transaction/prepare', entity).then(function (data){ | |
var serializedTx = data.data; | |
var signature = kp.sign(serializedTx.data); | |
var obj = {'data':serializedTx.data, 'signature':signature.toString()}; | |
console.log('nis', obj.data); | |
console.log(' js', convert.ua2hex(result)); | |
}, function(data) { | |
failedCb('prepare', data); | |
}); | |
/*/ | |
return $http.post('http://'+CURRENT_HOSTNAME+':'+nisPort+'/transaction/announce', obj).then(function (data){ | |
cb(data); | |
}, function(data) { | |
failedCb('announce', data); | |
}); | |
//*/ | |
}; | |
return o; | |
}]); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
define([ | |
'definitions', | |
'jquery', | |
'utils/Address', | |
'utils/CryptoHelpers', | |
'filters/filters', | |
'services/Transactions' | |
], function(angular, $, Address, CryptoHelpers) { | |
var mod = angular.module('walletApp.controllers'); | |
mod.controller('TxCosignatureCtrl', | |
["$scope", "$window", "$q", "$timeout", "Transactions", 'walletScope', 'parent', 'meta', | |
function($scope, $window, $q, $timeout, Transactions, walletScope, parent, meta) { | |
$scope.walletScope = walletScope; | |
$scope.storage = $window.localStorage; | |
$scope.storage.setDefault('txCosignDefaults', {}); | |
// load data from storage | |
$scope.common = { | |
'requiresKey': $scope.walletScope.sessionData.getRememberedKey() === undefined, | |
'password': '', | |
'privatekey': '', | |
}; | |
$scope.txCosignData = { | |
'fee': $scope.storage.getObject('txCosignDefaults').fee || 0, | |
'due': $scope.storage.getObject('txCosignDefaults').due || (24 * 60), | |
'multisigAccount': parent.otherTrans.signer, // inner tx signer is a multisig account | |
'multisigAccountAddress': Address.toAddress(parent.otherTrans.signer, $scope.walletScope.networkId), | |
'hash': meta.innerHash.data, // hash of an inner tx is needed | |
}; | |
// fix old default | |
var ver = $scope.storage.getObject('txCosignDefaults').ver; | |
if (! ver) { | |
$scope.txCosignData.due = 24 * 60; | |
} | |
$scope.$watchGroup(['common.password', 'common.privatekey'], function(nv,ov){ | |
$scope.invalidKeyOrPassword = false; | |
}); | |
$scope.okPressed = false; | |
$scope.ok = function txCosignOk() { | |
$scope.okPressed = true; | |
$timeout(function txCosignDeferred(){ | |
$scope._ok().then(function(){ | |
$scope.okPressed = false; | |
}, function(){ | |
$scope.okPressed = false; | |
}); | |
}); | |
}; | |
$scope._ok = function txCosign_Ok() { | |
// save most recent data | |
var orig = $scope.storage.getObject('txCosignDefaults') | |
$.extend(orig, { | |
'fee':$scope.txCosignData.fee, | |
'due':$scope.txCosignData.due, | |
'ver': 1 | |
}); | |
$scope.storage.setObject('txCosignDefaults', orig); | |
var rememberedKey = $scope.walletScope.sessionData.getRememberedKey(); | |
if (rememberedKey) { | |
$scope.common.privatekey = CryptoHelpers.decrypt(rememberedKey); | |
} else { | |
if (! CryptoHelpers.passwordToPrivatekey($scope.common, $scope.walletScope.networkId, $scope.walletScope.walletAccount) ) { | |
$scope.invalidKeyOrPassword = true; | |
return $q.resolve(0); | |
} | |
} | |
return Transactions.prepareSignature($scope.common, $scope.txCosignData, $scope.walletScope.nisPort, | |
function(data) { | |
if (data.status === 200) { | |
if (data.data.code >= 2) { | |
alert('failed when trying to send tx: ' + data.data.message); | |
} else { | |
$scope.$close(); | |
} | |
} | |
if (rememberedKey) { delete $scope.common.privatekey; } | |
}, | |
function(operation, data) { | |
// will do for now, will change it to modal later | |
alert('failed at '+operation + " " + data.data.error + " " + data.data.message); | |
if (rememberedKey) { delete $scope.common.privatekey; } | |
} | |
); | |
}; // $scope._ok | |
$scope.cancel = function () { | |
$scope.$dismiss(); | |
}; | |
} | |
]); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
define([ | |
'definitions', | |
'jquery', | |
'utils/CryptoHelpers', | |
'filters/filters', | |
], function(angular, $, CryptoHelpers) { | |
var mod = angular.module('walletApp.controllers'); | |
mod.controller('TxDetailsCtrl', | |
["$scope", 'walletScope', 'parent', 'tx', 'meta', | |
function($scope, walletScope, parent, tx, meta) { | |
$scope.walletScope = walletScope; | |
$scope.parent = parent; | |
$scope.tx = tx; | |
$scope.meta = meta; | |
$scope.ok = function () { | |
$scope.$close(); | |
}; | |
} | |
]); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
define([ | |
'definitions', | |
'jquery', | |
'utils/CryptoHelpers', | |
'sinks', | |
'filters/filters', | |
'services/Transactions' | |
], function(angular, $, CryptoHelpers, sinks) { | |
var mod = angular.module('walletApp.controllers'); | |
mod.controller('TxMosaicCtrl', | |
["$scope", "$window", "$q", "$timeout", "Transactions", 'walletScope', | |
function($scope, $window, $q, $timeout, Transactions, walletScope) { | |
$scope.walletScope = walletScope; | |
$scope.storage = $window.localStorage; | |
$scope.storage.setDefault('txMosaicDefaults', {}); | |
// begin tracking currently selected account and it's mosaics | |
$scope._updateCurrentAccount = function() { | |
var acct = $scope.walletScope.accountData.account.address | |
if ($scope.txMosaicData.isMultisig) { | |
acct = $scope.txMosaicData.multisigAccount.address; | |
} | |
$scope.currentAccount = acct; | |
}; | |
$scope.selectTab = function selectTab(v) { | |
if (v === 'multisig') { | |
$scope.txMosaicData.isMultisig = true; | |
} else { | |
$scope.txMosaicData.isMultisig = false; | |
} | |
$scope.updateCurrentAccountMosaics(); | |
}; | |
$scope.updateCurrentAccountMosaics = function updateCurrentAccountMosaics() { | |
$scope._updateCurrentAccount(); | |
var acct = $scope.currentAccount; | |
// we could do it without separate variable, but we want keys to be sorted | |
$scope.currentAccountMosaicNames = Object.keys($scope.walletScope.mosaicOwned[acct]).sort(); | |
$scope.selectedMosaic = "nem:xem"; | |
var ownedNamespaces = walletScope.namespaceOwned[acct]; | |
if (ownedNamespaces) { | |
$scope.txMosaicData.namespaceParent = ownedNamespaces[Object.keys(ownedNamespaces)[0]]; | |
} else { | |
alert("this account does not own any namespaces, try choosing non-multisig or a different account"); | |
} | |
}; | |
// end begin tracking currently selected account and it's mosaics | |
// load data from storage | |
$scope.common = { | |
'requiresKey': $scope.walletScope.sessionData.getRememberedKey() === undefined, | |
'password': '', | |
'privatekey': '', | |
}; | |
$scope.txMosaicData = { | |
'mosaicFeeSink': '', | |
'mosaicFee': 50000 * 1000000, | |
'mosaicName': '', | |
'namespaceParent': '', | |
'mosaicDescription': $scope.storage.getObject('txMosaicDefaults').mosaicDescription || '', | |
'properties': {'initialSupply':0, 'divisibility':0, 'transferable':true, 'supplyMutable':true}, | |
'levy':{'mosaic':null, 'address':'', 'feeType':1, 'fee':5}, | |
'fee': 0, | |
'innerFee': 0, | |
'due': $scope.storage.getObject('txMosaicDefaults').due || 60, | |
'isMultisig': ($scope.storage.getObject('txMosaicDefaults').isMultisig && walletScope.accountData.meta.cosignatoryOf.length > 0) || false, | |
'multisigAccount': walletScope.accountData.meta.cosignatoryOf.length == 0?'':walletScope.accountData.meta.cosignatoryOf[0] | |
}; | |
$scope.txMosaicData.mosaicFeeSink = sinks.mosaic[$scope.walletScope.networkId]; | |
$scope.hasLevy = false; | |
function updateFee() { | |
var entity = Transactions.prepareMosaicDefinition($scope.common, $scope.txMosaicData); | |
$scope.txMosaicData.fee = entity.fee; | |
if ($scope.txMosaicData.isMultisig) { | |
$scope.txMosaicData.innerFee = entity.otherTrans.fee; | |
} | |
} | |
$scope.$watchGroup(['common.password', 'common.privatekey'], function(nv,ov){ | |
$scope.invalidKeyOrPassword = false; | |
}); | |
$scope.$watchGroup(['txMosaicData.isMultisig'], function(nv, ov){ | |
updateFee(); | |
}); | |
$scope.$watch('selectedMosaic', function(){ | |
if ($scope.hasLevy) { | |
$scope.txMosaicData.levy.mosaic = $scope.walletScope.mosaicOwned[$scope.currentAccount][$scope.selectedMosaic].mosaicId; | |
} else { | |
$scope.txMosaicData.levy.mosaic = null; | |
} | |
}); | |
$scope.updateCurrentAccountMosaics(); | |
$scope.okPressed = false; | |
$scope.ok = function txMosaicOk() { | |
$scope.okPressed = true; | |
$timeout(function txMosaicDeferred(){ | |
$scope._ok().then(function(){ | |
$scope.okPressed = false; | |
}, function(){ | |
$scope.okPressed = false; | |
}); | |
}); | |
}; | |
$scope._ok = function txMosaic_Ok() { | |
var orig = $scope.storage.getObject('txMosaicDefaults'); | |
$.extend(orig, { | |
'due': $scope.txMosaicData.due, | |
'isMultisig': $scope.txMosaicData.isMultisig, | |
}); | |
$scope.storage.setObject('txMosaicDefaults', orig); | |
var rememberedKey = $scope.walletScope.sessionData.getRememberedKey(); | |
if (rememberedKey) { | |
$scope.common.privatekey = CryptoHelpers.decrypt(rememberedKey); | |
} else { | |
if (! CryptoHelpers.passwordToPrivatekey($scope.common, $scope.walletScope.networkId, $scope.walletScope.walletAccount) ) { | |
$scope.invalidKeyOrPassword = true; | |
return $q.resolve(0); | |
} | |
} | |
var entity = Transactions.prepareMosaicDefinition($scope.common, $scope.txMosaicData); | |
return Transactions.serializeAndAnnounceTransaction(entity, $scope.common, $scope.txMosaicData, $scope.walletScope.nisPort, | |
function(data) { | |
if (data.status === 200) { | |
if (data.data.code >= 2) { | |
alert('failed when trying to send tx: ' + data.data.message); | |
} else { | |
$scope.$close(); | |
} | |
} | |
if (rememberedKey) { delete $scope.common.privatekey; } | |
}, | |
function(operation, data) { | |
// will do for now, will change it to modal later | |
alert('failed at '+operation + " " + data.data.error + " " + data.data.message); | |
if (rememberedKey) { delete $scope.common.privatekey; } | |
} | |
); | |
}; // $scope._ok | |
$scope.cancel = function () { | |
$scope.$dismiss(); | |
}; | |
} | |
]); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
define([ | |
'definitions', | |
'jquery', | |
'utils/CryptoHelpers', | |
'filters/filters', | |
'services/Transactions' | |
], function(angular, $, CryptoHelpers) { | |
var mod = angular.module('walletApp.controllers'); | |
mod.controller('TxMosaicSupplyCtrl', | |
["$scope", "$window", "$timeout", "Transactions", 'walletScope', | |
function($scope, $window, $timeout, Transactions, walletScope) { | |
$scope.walletScope = walletScope; | |
$scope.storage = $window.localStorage; | |
$scope.storage.setDefault('txMosaicSupplyDefaults', {}); | |
// begin tracking currently selected account and it's mosaics | |
$scope._updateCurrentAccount = function() { | |
var acct = $scope.walletScope.accountData.account.address | |
if ($scope.txMosaicSupplyData.isMultisig) { | |
acct = $scope.txMosaicSupplyData.multisigAccount.address; | |
} | |
$scope.currentAccount = acct; | |
}; | |
$scope.selectTab = function selectTab(v) { | |
if (v === 'multisig') { | |
$scope.txMosaicSupplyData.isMultisig = true; | |
} else { | |
$scope.txMosaicSupplyData.isMultisig = false; | |
} | |
$scope.updateCurrentAccountMosaics(); | |
}; | |
$scope.updateCurrentAccountMosaics = function updateCurrentAccountMosaics() { | |
$scope._updateCurrentAccount(); | |
var acct = $scope.currentAccount; | |
$scope.currentAccountMosaicNames = Object.keys($scope.walletScope.mosaicOwned[acct]).sort(); | |
$scope.selectedMosaic = "nem:xem"; | |
}; | |
// end begin tracking currently selected account and it's mosaics | |
// load data from storage | |
$scope.common = { | |
'requiresKey': $scope.walletScope.sessionData.getRememberedKey() === undefined, | |
'password': '', | |
'privatekey': '', | |
}; | |
$scope.txMosaicSupplyData = { | |
'mosaic': '', | |
'supplyType': 1, | |
'delta': 0, | |
'fee': 0, | |
'innerFee': 0, | |
'due': $scope.storage.getObject('txMosaicSupplyDefaults').due || 60, | |
'isMultisig': ($scope.storage.getObject('txMosaicSupplyDefaults').isMultisig && walletScope.accountData.meta.cosignatoryOf.length > 0) || false, | |
'multisigAccount': walletScope.accountData.meta.cosignatoryOf.length == 0?'':walletScope.accountData.meta.cosignatoryOf[0] | |
}; | |
function updateFee() { | |
var entity = Transactions.prepareMosaicSupply($scope.common, $scope.txMosaicSupplyData); | |
$scope.txMosaicSupplyData.fee = entity.fee; | |
if ($scope.txMosaicSupplyData.isMultisig) { | |
$scope.txMosaicSupplyData.innerFee = entity.otherTrans.fee; | |
} | |
} | |
$scope.$watchGroup(['txMosaicSupplyData.isMultisig'], function(nv, ov){ | |
updateFee(); | |
}); | |
$scope.$watchGroup(['common.password', 'common.privatekey'], function(nv,ov){ | |
$scope.invalidKeyOrPassword = false; | |
}); | |
$scope.$watch('selectedMosaic', function(){ | |
$scope.txMosaicSupplyData.mosaic = $scope.walletScope.mosaicOwned[$scope.currentAccount][$scope.selectedMosaic].mosaicId; | |
}); | |
$scope.updateCurrentAccountMosaics(); | |
$scope.okPressed = false; | |
$scope.ok = function txMosaicSupplyOk() { | |
$scope.okPressed = true; | |
$timeout(function txMosaicSupplyDeferred(){ | |
$scope._ok().then(function(){ | |
$scope.okPressed = false; | |
}, function(){ | |
$scope.okPressed = false; | |
}); | |
}); | |
}; | |
$scope._ok = function txMosaicSupply_Ok() { | |
var orig = $scope.storage.getObject('txMosaicSupplyDefaults'); | |
$.extend(orig, { | |
'due': $scope.txMosaicSupplyData.due, | |
'isMultisig': $scope.txMosaicSupplyData.isMultisig, | |
}); | |
$scope.storage.setObject('txMosaicSupplyDefaults', orig); | |
var rememberedKey = $scope.walletScope.sessionData.getRememberedKey(); | |
if (rememberedKey) { | |
$scope.common.privatekey = CryptoHelpers.decrypt(rememberedKey); | |
} else { | |
if (! CryptoHelpers.passwordToPrivatekey($scope.common, $scope.walletScope.networkId, $scope.walletScope.walletAccount) ) { | |
$scope.invalidKeyOrPassword = true; | |
return $q.resolve(0); | |
} | |
} | |
var entity = Transactions.prepareMosaicSupply($scope.common, $scope.txMosaicSupplyData); | |
return Transactions.serializeAndAnnounceTransaction(entity, $scope.common, $scope.txMosaicSupplyData, $scope.walletScope.nisPort, | |
function(data) { | |
if (data.status === 200) { | |
if (data.data.code >= 2) { | |
alert('failed when trying to send tx: ' + data.data.message); | |
} else { | |
$scope.$close(); | |
} | |
if (rememberedKey) { delete $scope.common.privatekey; } | |
} | |
}, | |
function(operation, data) { | |
// will do for now, will change it to modal later | |
alert('failed at '+operation + " " + data.data.error + " " + data.data.message); | |
if (rememberedKey) { delete $scope.common.privatekey; } | |
} | |
); | |
}; // $scope._ok | |
$scope.cancel = function () { | |
$scope.$dismiss(); | |
}; | |
} | |
]); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
define([ | |
'definitions', | |
'jquery', | |
'utils/CryptoHelpers', | |
'sinks', | |
'filters/filters', | |
'services/Transactions' | |
], function(angular, $, CryptoHelpers, sinks) { | |
var mod = angular.module('walletApp.controllers'); | |
mod.controller('TxNamespaceCtrl', | |
["$scope", "$window", "$q", "$timeout", "Transactions", 'walletScope', | |
function($scope, $window, $q, $timeout, Transactions, walletScope) { | |
$scope.walletScope = walletScope; | |
$scope.storage = $window.localStorage; | |
$scope.storage.setDefault('txNamespaceDefaults', {}); | |
// begin tracking currently selected account | |
$scope._updateCurrentAccount = function() { | |
var acct = $scope.walletScope.accountData.account.address | |
if ($scope.txNamespaceData.isMultisig) { | |
acct = $scope.txNamespaceData.multisigAccount.address; | |
} | |
$scope.currentAccount = acct; | |
}; | |
$scope.selectTab = function selectTab(v) { | |
if (v === 'multisig') { | |
$scope.txNamespaceData.isMultisig = true; | |
} else { | |
$scope.txNamespaceData.isMultisig = false; | |
} | |
$scope._updateCurrentAccount(); | |
}; | |
// end begin tracking currently selected account | |
// load data from storage | |
$scope.common = { | |
'requiresKey': $scope.walletScope.sessionData.getRememberedKey() === undefined, | |
'password': '', | |
'privatekey': '', | |
}; | |
$scope.txNamespaceData = { | |
'rentalFeeSink': '', | |
'rentalFee': 0, | |
'namespaceName': '', | |
'namespaceParent': null, | |
'fee': 0, | |
'innerFee': 0, | |
'due': $scope.storage.getObject('txNamespaceDefaults').due || 60, | |
'isMultisig': ($scope.storage.getObject('txNamespaceDefaults').isMultisig && walletScope.accountData.meta.cosignatoryOf.length > 0) || false, | |
'multisigAccount': walletScope.accountData.meta.cosignatoryOf.length == 0?'':walletScope.accountData.meta.cosignatoryOf[0] | |
}; | |
$scope.txNamespaceData.rentalFeeSink = sinks.namespace[$scope.walletScope.networkId]; | |
$scope.namespaceLevel3 = function(elem) { | |
return elem.fqn.split('.').length < 3 | |
}; | |
function updateFee() { | |
var entity = Transactions.prepareNamespace($scope.common, $scope.txNamespaceData); | |
$scope.txNamespaceData.fee = entity.fee; | |
if ($scope.txNamespaceData.isMultisig) { | |
$scope.txNamespaceData.innerFee = entity.otherTrans.fee; | |
} | |
} | |
$scope.$watchGroup(['txNamespaceData.namespaceName', 'txNamespaceData.namespaceParent', 'txNamespaceData.isMultisig'], function(nv, ov){ | |
updateFee(); | |
}); | |
$scope.$watchGroup(['common.password', 'common.privatekey'], function(nv,ov){ | |
$scope.invalidKeyOrPassword = false; | |
}); | |
$scope.$watch('txNamespaceData.namespaceParent', function(nv, ov){ | |
if ($scope.txNamespaceData.namespaceParent) { | |
$scope.txNamespaceData.rentalFee = 5000 * 1000000; | |
} else { | |
$scope.txNamespaceData.rentalFee = 50000 * 1000000; | |
} | |
}); | |
$scope._updateCurrentAccount(); | |
$scope.okPressed = false; | |
$scope.ok = function txNamespaceOk() { | |
$scope.okPressed = true; | |
$timeout(function txNamespaceDeferred(){ | |
$scope._ok().then(function(){ | |
$scope.okPressed = false; | |
}, function(){ | |
$scope.okPressed = false; | |
}); | |
}); | |
}; | |
$scope._ok = function txNamespace_Ok() { | |
var orig = $scope.storage.getObject('txNamespaceDefaults'); | |
$.extend(orig, { | |
'due': $scope.txNamespaceData.due, | |
'isMultisig': $scope.txNamespaceData.isMultisig, | |
}); | |
$scope.storage.setObject('txNamespaceDefaults', orig); | |
var rememberedKey = $scope.walletScope.sessionData.getRememberedKey(); | |
if (rememberedKey) { | |
$scope.common.privatekey = CryptoHelpers.decrypt(rememberedKey); | |
} else { | |
if (! CryptoHelpers.passwordToPrivatekey($scope.common, $scope.walletScope.networkId, $scope.walletScope.walletAccount) ) { | |
$scope.invalidKeyOrPassword = true; | |
return $q.resolve(0); | |
} | |
} | |
var entity = Transactions.prepareNamespace($scope.common, $scope.txNamespaceData); | |
return Transactions.serializeAndAnnounceTransaction(entity, $scope.common, $scope.txNamespaceData, $scope.walletScope.nisPort, | |
function(data) { | |
if (data.status === 200) { | |
if (data.data.code >= 2) { | |
alert('failed when trying to send tx: ' + data.data.message); | |
} else { | |
$scope.$close(); | |
} | |
} | |
if (rememberedKey) { delete $scope.common.privatekey; } | |
}, | |
function(operation, data) { | |
// will do for now, will change it to modal later | |
alert('failed at '+operation + " " + data.data.error + " " + data.data.message); | |
if (rememberedKey) { delete $scope.common.privatekey; } | |
} | |
); | |
}; // $scope._ok | |
$scope.cancel = function () { | |
$scope.$dismiss(); | |
}; | |
} | |
]); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
define([ | |
'definitions', | |
'jquery', | |
'utils/CryptoHelpers', | |
'utils/Address', | |
'filters/filters', | |
'services/Transactions' | |
], function(angular, $, CryptoHelpers, Address) { | |
var mod = angular.module('walletApp.controllers'); | |
mod.controller('TxTransferCtrl', | |
["$scope", "$window", "$http", "$q", "$timeout", "Transactions", 'walletScope', | |
function($scope, $window, $http, $q, $timeout, Transactions, walletScope) { | |
$scope.walletScope = walletScope; | |
$scope.encryptDisabled = false; | |
$scope.storage = $window.localStorage; | |
$scope.storage.setDefault('txTransferDefaults', {}); | |
// load data from storage | |
$scope.common = { | |
'requiresKey': $scope.walletScope.sessionData.getRememberedKey() === undefined, | |
'password': '', | |
'privatekey': '', | |
}; | |
$scope.txTransferData = { | |
'recipient': $scope.storage.getObject('txTransferDefaults').recipient || '', | |
'amount': $scope.storage.getObject('txTransferDefaults').amount, | |
'fee': $scope.storage.getObject('txTransferDefaults').fee || 0, | |
'innerFee': 0, | |
'due': $scope.storage.getObject('txTransferDefaults').due || 60, | |
'message': $scope.storage.getObject('txTransferDefaults').message || '', | |
'encryptMessage': $scope.storage.getObject('txTransferDefaults').encryptMessage || false, | |
'isMultisig': ($scope.storage.getObject('txTransferDefaults').isMultisig && walletScope.accountData.meta.cosignatoryOf.length > 0) || false, | |
'multisigAccount': walletScope.accountData.meta.cosignatoryOf.length == 0?'':walletScope.accountData.meta.cosignatoryOf[0] | |
}; | |
function updateFee() { | |
var entity = Transactions.prepareTransfer($scope.common, $scope.txTransferData); | |
$scope.txTransferData.fee = entity.fee; | |
if ($scope.txTransferData.isMultisig) { | |
$scope.txTransferData.innerFee = entity.otherTrans.fee; | |
} | |
} | |
$scope.$watchGroup(['txTransferData.amount', 'txTransferData.message', 'txTransferData.isMultisig'], function(nv, ov){ | |
updateFee(); | |
if ($scope.txTransferData.isMultisig) { | |
$scope.txTransferData.encryptMessage = false; | |
$scope.encryptDisabled = true; | |
} else { | |
$scope.encryptDisabled = false; | |
} | |
}); | |
$scope.$watchGroup(['common.password', 'common.privatekey'], function(nv,ov){ | |
$scope.invalidKeyOrPassword = false; | |
}); | |
$scope.recipientCache = {}; | |
$scope.$watch('txTransferData.recipient', function(nv, ov){ | |
if (! nv) { | |
return; | |
} | |
var recipientAddress = nv.toUpperCase().replace(/-/g, ''); | |
var nisPort = $scope.walletScope.nisPort; | |
var obj = {'params':{'address':recipientAddress}}; | |
if (! (recipientAddress in $scope.recipientCache)) { | |
var _uriParser = document.createElement('a'); | |
_uriParser.href = $scope.walletScope.sessionData.getNode().uri; | |
if (Address.isFromNetwork(recipientAddress, $scope.walletScope.networkId)) { | |
console.log(recipientAddress, $scope.walletScope.networkId); | |
$http.get('http://'+_uriParser.hostname+':'+nisPort+'/account/get', obj).then(function (data){ | |
$scope.recipientCache[recipientAddress] = data.data.account.publicKey; | |
}); | |
} | |
} | |
}); | |
$scope.okPressed = false; | |
$scope.ok = function txTransferOk() { | |
$scope.okPressed = true; | |
$timeout(function txTransferDeferred(){ | |
$scope._ok().then(function(){ | |
$scope.okPressed = false; | |
}, function(){ | |
$scope.okPressed = false; | |
}); | |
}); // timeout | |
}; | |
$scope._ok = function txTransfer_Ok() { | |
// save most recent data | |
// BUG: tx data is saved globally not per wallet... | |
var orig = $scope.storage.getObject('txTransferDefaults'); | |
$.extend(orig, { | |
'recipient':$scope.txTransferData.recipient, | |
'amount':$scope.txTransferData.amount, | |
'fee':$scope.txTransferData.fee, | |
'due':$scope.txTransferData.due, | |
'message':$scope.txTransferData.message, | |
'encryptMessage':$scope.txTransferData.encryptMessage, | |
'isMultisig':$scope.txTransferData.isMultisig, | |
}); | |
$scope.storage.setObject('txTransferDefaults', orig); | |
var recipientAddress = $scope.txTransferData.recipient.toUpperCase().replace(/-/g, ''); | |
$scope.txTransferData.recipientPubKey = $scope.recipientCache[recipientAddress]; | |
if ($scope.txTransferData.encryptMessage && !$scope.txTransferData.recipientPubKey) { | |
return $scope.walletScope.displayWarning("Encrypted message selected, but couldn't find public key of a recipient"); | |
} | |
var rememberedKey = $scope.walletScope.sessionData.getRememberedKey(); | |
if (rememberedKey) { | |
$scope.common.privatekey = CryptoHelpers.decrypt(rememberedKey); | |
} else { | |
if (! CryptoHelpers.passwordToPrivatekey($scope.common, $scope.walletScope.networkId, $scope.walletScope.walletAccount) ) { | |
$scope.invalidKeyOrPassword = true; | |
return $q.resolve(0); | |
} | |
} | |
var entity = Transactions.prepareTransfer($scope.common, $scope.txTransferData); | |
return Transactions.serializeAndAnnounceTransaction(entity, $scope.common, $scope.txTransferData, $scope.walletScope.nisPort, | |
function(data) { | |
if (data.status === 200) { | |
if (data.data.code >= 2) { | |
alert('failed when trying to send tx: ' + data.data.message); | |
} else { | |
$scope.$close(); | |
} | |
} | |
if (rememberedKey) { delete $scope.common.privatekey; } | |
}, | |
function(operation, data) { | |
// will do for now, will change it to modal later | |
alert('failed at '+operation + " " + data.data.error + " " + data.data.message); | |
if (rememberedKey) { delete $scope.common.privatekey; } | |
} | |
); | |
}; // $scope._ok | |
$scope.cancel = function () { | |
$scope.$dismiss(); | |
}; | |
} | |
]); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
define([ | |
'definitions', | |
'jquery', | |
'utils/CryptoHelpers', | |
'filters/filters', | |
'services/Transactions' | |
], function(angular, $, CryptoHelpers) { | |
var mod = angular.module('walletApp.controllers'); | |
mod.controller('TxTransferV2Ctrl', | |
["$scope", "$window", "$q", "$timeout", "Transactions", 'walletScope', | |
function($scope, $window, $q, $timeout, Transactions, walletScope) { | |
$scope.walletScope = walletScope; | |
$scope.counter = 1; | |
$scope.storage = $window.localStorage; | |
$scope.storage.setDefault('txTransfer2Defaults', {}); | |
// begin tracking currently selected account and it's mosaics | |
$scope._updateCurrentAccount = function() { | |
var acct = $scope.walletScope.accountData.account.address | |
if ($scope.txTransferV2Data.isMultisig) { | |
acct = $scope.txTransferV2Data.multisigAccount.address; | |
} | |
$scope.currentAccount = acct; | |
}; | |
$scope.selectTab = function selectTab(v) { | |
if (v === 'multisig') { | |
$scope.txTransferV2Data.isMultisig = true; | |
} else { | |
$scope.txTransferV2Data.isMultisig = false; | |
} | |
$scope.updateCurrentAccountMosaics(); | |
}; | |
$scope.updateCurrentAccountMosaics = function updateCurrentAccountMosaics() { | |
$scope._updateCurrentAccount(); | |
var acct = $scope.currentAccount; | |
$scope.currentAccountMosaicNames = Object.keys($scope.walletScope.mosaicOwned[acct]).sort(); | |
$scope.selectedMosaic = "nem:xem"; | |
}; | |
// end begin tracking currently selected account and it's mosaics | |
$scope.removeMosaic = function removeMosaic(index) { | |
$scope.txTransferV2Data.mosaics.splice(index, 1); | |
}; | |
function mosaicIdToName(mosaicId) { | |
return mosaicId.namespaceId + ":" + mosaicId.name; | |
} | |
$scope.attachMosaic = function attachMosaic() { | |
var acct = $scope.currentAccount; | |
var mosaic = $scope.walletScope.mosaicOwned[acct][$scope.selectedMosaic]; | |
var elem = $.grep($scope.txTransferV2Data.mosaics, function(w){ return mosaicIdToName(mosaic.mosaicId) === mosaicIdToName(w.mosaicId); }); | |
if (elem.length === 0) { | |
$scope.counter += 1; | |
$scope.txTransferV2Data.mosaics.push({'mosaicId':mosaic['mosaicId'], 'quantity':0, 'gid':'mos_id_'+$scope.counter}); | |
} else { | |
$('#'+elem[0].gid).focus(); | |
} | |
}; | |
// load data from storage | |
$scope.common = { | |
'requiresKey': $scope.walletScope.sessionData.getRememberedKey() === undefined, | |
'password': '', | |
'privatekey': '', | |
}; | |
$scope.txTransferV2Data = { | |
'recipient': $scope.storage.getObject('txTransfer2Defaults').recipient || '', | |
'multiplier': $scope.storage.getObject('txTransfer2Defaults').multiplier || 1, | |
'amount': 0, | |
'fee': $scope.storage.getObject('txTransfer2Defaults').fee || 0, | |
'innerFee': 0, | |
'due': $scope.storage.getObject('txTransfer2Defaults').due || 60, | |
'message': $scope.storage.getObject('txTransfer2Defaults').message || '', | |
'isMultisig': ($scope.storage.getObject('txTransfer2Defaults').isMultisig && walletScope.accountData.meta.cosignatoryOf.length > 0) || false, | |
'multisigAccount': walletScope.accountData.meta.cosignatoryOf.length == 0?'':walletScope.accountData.meta.cosignatoryOf[0], | |
'mosaics': [ {'mosaicId':{'namespaceId':'nem', 'name':'xem'}, 'quantity':0, 'gid':'mos_id_0'} ] | |
}; | |
function updateFee() { | |
var entity = Transactions.prepareTransferV2($scope.common, $scope.walletScope.mosaicDefinitionMetaDataPair, $scope.txTransferV2Data); | |
$scope.txTransferV2Data.fee = entity.fee; | |
if ($scope.txTransferV2Data.isMultisig) { | |
$scope.txTransferV2Data.innerFee = entity.otherTrans.fee; | |
} | |
} | |
$scope.$watchGroup(['txTransferV2Data.message', 'txTransferV2Data.isMultisig'], function(nv, ov){ | |
updateFee(); | |
}); | |
$scope.$watchGroup(['common.password', 'common.privatekey'], function(nv,ov){ | |
$scope.invalidKeyOrPassword = false; | |
}); | |
$scope.$watch('txTransferV2Data.mosaics', function(){ | |
updateFee(); | |
}, true); | |
$scope.$watch('multiplier', function(){ | |
$scope.txTransferV2Data.amount = parseInt($scope.txTransferV2Data.multiplier * 1000000, 10) || 0; | |
}); | |
$scope.updateCurrentAccountMosaics(); | |
$scope.okPressed = false; | |
$scope.ok = function txTransferV2Ok() { | |
$scope.okPressed = true; | |
$timeout(function txTransferV2Deferred(){ | |
$scope._ok().then(function(){ | |
$scope.okPressed = false; | |
}, function(){ | |
$scope.okPressed = false; | |
}); | |
}); | |
}; | |
$scope._ok = function txTransferV2_Ok() { | |
// save most recent data | |
// BUG: tx data is saved globally not per wallet... | |
var orig = $scope.storage.getObject('txTransfer2Defaults'); | |
$.extend(orig, { | |
'recipient': $scope.txTransferV2Data.recipient, | |
'multiplier': $scope.txTransferV2Data.multiplier, | |
'fee': $scope.txTransferV2Data.fee, | |
'due': $scope.txTransferV2Data.due, | |
'message': $scope.txTransferV2Data.message, | |
'isMultisig': $scope.txTransferV2Data.isMultisig, | |
}); | |
$scope.storage.setObject('txTransfer2Defaults', orig); | |
// | |
var rememberedKey = $scope.walletScope.sessionData.getRememberedKey(); | |
if (rememberedKey) { | |
$scope.common.privatekey = CryptoHelpers.decrypt(rememberedKey); | |
} else { | |
if (! CryptoHelpers.passwordToPrivatekey($scope.common, $scope.walletScope.networkId, $scope.walletScope.walletAccount) ) { | |
$scope.invalidKeyOrPassword = true; | |
return $q.resolve(0); | |
} | |
} | |
var entity = Transactions.prepareTransferV2($scope.common, $scope.walletScope.mosaicDefinitionMetaDataPair, $scope.txTransferV2Data); | |
return Transactions.serializeAndAnnounceTransaction(entity, $scope.common, $scope.txTransferV2Data, $scope.walletScope.nisPort, | |
function(data) { | |
if (data.status === 200) { | |
if (data.data.code >= 2) { | |
alert('failed when trying to send tx: ' + data.data.message); | |
} else { | |
$scope.$close(); | |
} | |
} | |
if (rememberedKey) { delete $scope.common.privatekey; } | |
}, | |
function(operation, data) { | |
// will do for now, will change it to modal later | |
alert('failed at '+operation + " " + data.data.error + " " + data.data.message); | |
if (rememberedKey) { delete $scope.common.privatekey; } | |
} | |
); | |
}; // $scope._ok | |
$scope.cancel = function () { | |
$scope.$dismiss(); | |
}; | |
} | |
]); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
define([ | |
'definitions', | |
'jquery', | |
'utils/Connector', | |
'utils/CryptoHelpers', | |
'utils/KeyPair', | |
'utils/TransactionType', | |
// angular related | |
'controllers/dialogWarning', | |
'controllers/txTransfer', | |
'controllers/txTransferV2', | |
'controllers/txNamespace', | |
'controllers/txMosaic', | |
'controllers/txMosaicSupply', | |
'controllers/txCosignature', | |
'controllers/txDetails', | |
'controllers/msgDecode', | |
'filters/filters', | |
'services/Transactions', | |
'services/SessionData' | |
], function(angular, $, Connector, CryptoHelpers, KeyPair, TransactionType) { | |
var mod = angular.module('walletApp.controllers'); | |
mod.controller('WalletCtrl', | |
["$scope", "$http", "$location", "$window", "$timeout", "$routeParams", "$uibModal", "sessionData", | |
function WalletCtrl($scope, $http, $location, $window, $timeout, $routeParams, $uibModal, sessionData) { | |
if (sessionData.getNisPort() === 0 || !sessionData.getNetworkId() || !sessionData.getNode()) { | |
$location.path('/login'); | |
return; | |
} | |
$scope.$on('$locationChangeStart', function( event ) { | |
if ($scope.connector) { | |
sessionData.setRememberedKey(undefined); | |
$scope.connector.close(); | |
} | |
}); | |
$scope.connector = undefined; | |
$scope.storage = $window.localStorage; | |
$scope.storage.setDefault('txTransferDefaults', {}); | |
var elem = $.grep($scope.storage.getObject('wallets'), function(w){ return w.name == $routeParams.walletName; }); | |
$scope.walletAccount = elem.length == 1 ? elem[0].accounts[0] : null; | |
$scope.nisPort = sessionData.getNisPort(); | |
$scope.networkId = sessionData.getNetworkId(); | |
$scope.nisHeight = 0; | |
$scope.sessionData = sessionData; | |
$scope.activeWalletTab = 0; | |
$scope.setWalletTab = function setWalletTab(index) { | |
$scope.activeWalletTab = index; | |
}; | |
function mosaicIdToName(mosaicId) { | |
return mosaicId.namespaceId + ":" + mosaicId.name; | |
} | |
// ==== ==== ==== ==== dialogs | |
// in case of dialogs, I'm passing the scope, we could ofc use $scope.$parent, | |
// in descendant controllers, but this makes it more verbose and easier to follow | |
// it's also easier, than passing proper elements from current scope. | |
$scope.displayWarning = function(warningMsg) { | |
var modalInstance = $uibModal.open({ | |
animation: true, | |
templateUrl: 'views/dialogWarning.html', | |
controller: 'DialogWarningCtrl', | |
backdrop: true, | |
resolve: { | |
warningMsg: function() { return warningMsg; } | |
} | |
}); | |
return modalInstance.result; | |
}; | |
$scope.displayTransferDialog = function() { | |
var modalInstance = $uibModal.open({ | |
animation: false, | |
templateUrl: 'views/txTransfer.html', | |
controller: 'TxTransferCtrl', | |
backdrop: false, | |
resolve: { | |
walletScope: function() { | |
return $scope; | |
} | |
} | |
}); | |
}; | |
$scope.displayTransferV2Dialog = function() { | |
if ($scope.networkId === 104 && $scope.nisHeight < 440000) { | |
$scope.displayWarning("v2 transfers will be available after fork at 440k"); | |
return; | |
} | |
var modalInstance = $uibModal.open({ | |
animation: false, | |
templateUrl: 'views/txTransferV2.html', | |
controller: 'TxTransferV2Ctrl', | |
backdrop: false, | |
resolve: { | |
walletScope: function() { | |
return $scope; | |
} | |
} | |
}); | |
}; | |
$scope.displayNamespaceDialog = function() { | |
if ($scope.networkId === 104 && $scope.nisHeight < 440000) { | |
$scope.displayWarning("namespaces will be available after fork at 440k"); | |
return; | |
} | |
var modalInstance = $uibModal.open({ | |
animation: false, | |
templateUrl: 'views/txNamespace.html', | |
controller: 'TxNamespaceCtrl', | |
backdrop: false, | |
resolve: { | |
walletScope: function() { | |
return $scope; | |
} | |
} | |
}); | |
}; | |
$scope.displayMosaicDialog = function() { | |
if ($scope.networkId === 104 && $scope.nisHeight < 440000) { | |
$scope.displayWarning("mosaics will be available after fork at 440k"); | |
return; | |
} | |
var modalInstance = $uibModal.open({ | |
animation: false, | |
templateUrl: 'views/txMosaic.html', | |
controller: 'TxMosaicCtrl', | |
backdrop: false, | |
resolve: { | |
walletScope: function() { | |
return $scope; | |
} | |
} | |
}); | |
}; | |
$scope.displayMosaicSupplyDialog = function() { | |
if ($scope.networkId === 104 && $scope.nisHeight < 440000) { | |
$scope.displayWarning("v2 transfers will be available after fork at 440k"); | |
return; | |
} | |
var modalInstance = $uibModal.open({ | |
animation: false, | |
templateUrl: 'views/txMosaicSupply.html', | |
controller: 'TxMosaicSupplyCtrl', | |
backdrop: false, | |
resolve: { | |
walletScope: function() { | |
return $scope; | |
} | |
} | |
}); | |
}; | |
$scope.displayDecodeMessage = function(tx) { | |
if (!tx || !tx.message || tx.message.type !== 2) { | |
alert("missing transaction data"); | |
return; | |
} | |
var modalInstance = $uibModal.open({ | |
animation: false, | |
templateUrl: 'views/msgDecode.html', | |
controller: 'MsgDecodeCtrl', | |
backdrop: false, | |
size: 'lg', | |
resolve: { | |
walletScope: function() { | |
return $scope; | |
}, | |
tx: function() { | |
return tx; | |
} | |
} | |
}); | |
}; | |
$scope.cosignTransaction = function(parentTx, tx, meta) { | |
//console.log("cosignTransaction parent", parentTx, "\ncosignTransaction inner", tx, "\ncosignTransaction meta", meta); | |
var modalInstance = $uibModal.open({ | |
animation: false, | |
templateUrl: 'views/txCosignature.html', | |
controller: 'TxCosignatureCtrl', | |
backdrop: false, | |
resolve: { | |
walletScope: function() { | |
return $scope; | |
}, | |
parent: function() { | |
return parentTx; | |
}, | |
meta: function() { | |
return meta; | |
} | |
} | |
}); | |
}; | |
$scope.displayTransactionDetails = function(parentTx, tx, meta) { | |
var modalInstance = $uibModal.open({ | |
animation: false, | |
templateUrl: 'views/txDetails.html', | |
controller: 'TxDetailsCtrl', | |
backdrop: false, | |
size: 'lg', | |
resolve: { | |
walletScope: function() { | |
return $scope; | |
}, | |
parent: function() { | |
return parentTx; | |
}, | |
tx: function() { | |
return tx; | |
}, | |
meta: function() { | |
return meta; | |
} | |
} | |
}); | |
}; | |
// ==== ==== ==== ==== cached data | |
$scope.accountData = {}; | |
$scope.transactions = []; | |
$scope.unconfirmedSize = 0; | |
$scope.unconfirmed = {}; | |
$scope.mosaicDefinitionMetaDataPair = {}; | |
$scope.mosaicDefinitionMetaDataPairSize = 0; | |
$scope.mosaicOwned = {}; | |
$scope.mosaicOwnedSize = {}; | |
$scope.namespaceOwned = {}; | |
$scope.getLevy = function getLevy(d) { | |
var mosaicName = mosaicIdToName(d.mosaicId); | |
if (!(mosaicName in $scope.mosaicDefinitionMetaDataPair)) { | |
return false; | |
} | |
var mosaicDefinitionMetaDataPair = $scope.mosaicDefinitionMetaDataPair[mosaicName]; | |
return mosaicDefinitionMetaDataPair.mosaicDefinition.levy; | |
}; | |
$scope.mosaicIdToName = mosaicIdToName; | |
// ==== ==== ==== ==== connection to the server and websocket handlers | |
// if we got wallet name let's set up everything... | |
if (elem.length == 1) { | |
$scope.name = elem[0].name; | |
$scope.account = elem[0].accounts[0].address; | |
$scope.connectionStatus = "connecting"; | |
var _uriParser = document.createElement('a'); | |
_uriParser.href = $scope.sessionData.getNode().uri; | |
$http.get('http://'+_uriParser.hostname+':'+$scope.nisPort+'/chain/height').then(function (data){ | |
$scope.nisHeight = data.height; | |
}); | |
var connector = Connector(sessionData.getNode(), elem[0].accounts[0].address); | |
connector.connect(function(){ | |
$timeout(function(){ | |
$scope.connectionStatus = "connected"; | |
}, 0); | |
function unconfirmedCallback(d) { | |
// we could first check if coming tx is already in unconfirmed, but | |
// tx itself can change in case of multisig txes,, so don't do that check | |
$timeout(function() { | |
$scope.unconfirmed[d.meta.hash.data] = d; | |
$scope.unconfirmedSize = Object.keys($scope.unconfirmed).length; | |
}, 0); | |
//console.log("unconfirmed data: ", Object.keys($scope.unconfirmed).length, d); | |
var audio = new Audio('/lightwallet/ding.ogg'); | |
audio.play(); | |
} | |
function confirmedCallback(d) { | |
$timeout(function() { | |
delete $scope.unconfirmed[d.meta.hash.data]; | |
$scope.unconfirmedSize = Object.keys($scope.unconfirmed).length; | |
$scope.transactions.push(d); | |
}, 0); | |
// console.log(">> transactions data: ", d); | |
var audio = new Audio('/lightwallet/ding2.ogg'); | |
audio.play(); | |
} | |
function mosaicDefinitionCallback(d) { | |
$timeout(function() { | |
$scope.mosaicDefinitionMetaDataPair[mosaicIdToName(d.mosaicDefinition.id)] = d; | |
$scope.mosaicDefinitionMetaDataPairSize = Object.keys($scope.mosaicDefinitionMetaDataPair).length; | |
}, 0); | |
} | |
function mosaicCallback(d, address) { | |
$timeout(function() { | |
var mosaicName = mosaicIdToName(d.mosaicId); | |
if (! (address in $scope.mosaicOwned)) { | |
$scope.mosaicOwned[address] = {}; | |
} | |
$scope.mosaicOwned[address][mosaicName] = d; | |
$scope.mosaicOwnedSize[address] = Object.keys($scope.mosaicOwned[address]).length; | |
}, 0); | |
} | |
function namespaceCallback(d, address) { | |
$timeout(function() { | |
var namespaceName = d.fqn; | |
if (! (address in $scope.namespaceOwned)) { | |
$scope.namespaceOwned[address] = {}; | |
} | |
$scope.namespaceOwned[address][namespaceName] = d; | |
}, 0); | |
} | |
connector.on('errors', function(name, d) { | |
console.log(d); | |
alert(d.error + " " + d.message); | |
}); | |
connector.on('account', function(d) { | |
$timeout(function(){ | |
$scope.accountData = d; | |
//console.log("account data: ", $scope.accountData); | |
// prepare callback for multisig accounts | |
for (var elem of $scope.accountData.meta.cosignatoryOf) { | |
connector.onConfirmed(confirmedCallback, elem.address); | |
connector.onUnconfirmed(unconfirmedCallback, elem.address); | |
connector.onNamespace(namespaceCallback, elem.address); | |
connector.onMosaicDefinition(mosaicDefinitionCallback, elem.address); | |
connector.onMosaic(mosaicCallback, elem.address); | |
} | |
// we need to subscribe to multisig accounts, in order to receive notifications | |
// about transactions involving those accounts | |
for (var elem of $scope.accountData.meta.cosignatoryOf) { | |
// no need to check return value, as if we're here, it means we're already connected | |
connector.subscribeToMultisig(elem.address); | |
// we don't need to request that, as request for accounts' unconfirmed txes should include those needed cosingature | |
//connector.requestUnconfirmedTransactions(elem.address); | |
connector.requestAccountNamespaces(elem.address); | |
connector.requestAccountMosaicDefinitions(elem.address); | |
connector.requestAccountMosaics(elem.address); | |
} | |
}, 0); | |
}); | |
connector.on('recenttransactions', function(d) { | |
d.data.reverse(); | |
$timeout(function(){ | |
$scope.transactions = d.data; | |
}, 0); | |
//console.log("recenttransactions data: ", d); | |
}); | |
connector.on('newblocks', function(blockHeight) { | |
$scope.nisHeight = blockHeight.height; | |
var cleanedTransactions = []; | |
$.each($scope.transactions, function(idx, tx) { | |
if (tx.meta.height < blockHeight.height) { | |
cleanedTransactions.push(tx); | |
} else { | |
//console.log("OK, ", blockHeight, "removed tx: ", tx); | |
} | |
}); | |
$timeout(function(){ | |
$scope.transactions = cleanedTransactions; | |
}, 0); | |
}); | |
connector.onConfirmed(confirmedCallback); | |
connector.onUnconfirmed(unconfirmedCallback); | |
connector.onNamespace(namespaceCallback); | |
connector.onMosaicDefinition(mosaicDefinitionCallback); | |
connector.onMosaic(mosaicCallback); | |
connector.requestAccountData(); | |
connector.requestAccountNamespaces(); | |
connector.requestAccountMosaicDefinitions(); | |
connector.requestAccountTransactions(); | |
connector.requestAccountMosaics(); | |
}); | |
$scope.connector = connector; | |
} | |
}] | |
); | |
function txTypeToName(id) { | |
switch (id) { | |
case TransactionType.Transfer: return 'Transfer'; | |
case TransactionType.ImportanceTransfer: return 'ImportanceTransfer'; | |
case TransactionType.MultisigModification: return 'MultisigModification'; | |
case TransactionType.ProvisionNamespace: return 'ProvisionNamespace'; | |
case TransactionType.MosaicDefinition: return 'MosaicDefinition'; | |
case TransactionType.MosaicSupply: return 'MosaicSupply'; | |
default: return 'Unknown_'+id; | |
} | |
} | |
function needsSignature(multisigTransaction, accountData) { | |
// we're issuer | |
if (multisigTransaction.transaction.signer === accountData.account.publicKey) { | |
return false; | |
} | |
// check if we're already on list of signatures | |
for (var elem of multisigTransaction.transaction.signatures) { | |
if (elem.signer === accountData.account.publicKey) { | |
return false; | |
} | |
} | |
return true; | |
} | |
mod.directive('tagtransaction', function() { | |
return { | |
restrict: 'E', | |
scope: { | |
d: '=', | |
tooltipPosition: '=', | |
accountData: '=', | |
}, | |
// we're passing ng-include as a template in order to dynamically select proper templates, | |
// the selection itself is done below in assignment to scope.templateUri | |
template: '<ng-include src="templateUri"/>', | |
link: function postLink(scope) { | |
if (scope.d.transaction.type === 4100) { | |
scope.tx = scope.d.transaction.otherTrans; | |
scope.meta = scope.d.meta; | |
scope.parent = scope.d.transaction; | |
} else { | |
scope.tx = scope.d.transaction; | |
scope.meta = scope.d.meta; | |
scope.parent = undefined; | |
} | |
scope.confirmed = !(scope.meta.height === Number.MAX_SAFE_INTEGER); | |
// if multisig and not confirmed, check if we need to cosign | |
scope.needsSignature = scope.parent && !scope.confirmed && scope.accountData && needsSignature(scope.d, scope.accountData); | |
scope.templateName = txTypeToName(scope.tx.type); | |
scope.templateUri = 'views/line'+scope.templateName+'.html'; | |
scope.cosignCallback = scope.$parent.cosignTransaction; | |
scope.displayTransactionDetails = scope.$parent.displayTransactionDetails; | |
scope.networkId = scope.$parent.networkId; | |
} | |
}; | |
}); | |
mod.directive('tagdetails', ["$http", function($http) { | |
return { | |
restrict: 'E', | |
scope: { | |
parent: '=', | |
tx: '=', | |
meta: '=' | |
}, | |
template: '<ng-include src="templateUri"/>', | |
link: function postLink(scope) { | |
scope.transactionTypeName = txTypeToName(scope.tx.type); | |
scope.templateUri = 'views/details'+scope.transactionTypeName+'.html'; | |
scope.decode = function(tx) { | |
if (tx.message && tx.message.type === 2) { | |
scope.$parent.walletScope.displayDecodeMessage(tx); | |
} | |
}; | |
// scope.$parent == TxDetailsCtrl | |
scope.mosaicDefinitionMetaDataPair = scope.$parent.walletScope.mosaicDefinitionMetaDataPair; | |
scope.getLevy = scope.$parent.walletScope.getLevy; | |
scope.mosaicIdToName = scope.$parent.walletScope.mosaicIdToName; | |
scope.networkId = scope.$parent.walletScope.networkId; | |
scope.recipientPublicKey = ''; | |
scope.gettingRecipientInfo = true; | |
scope.requiresKey = scope.$parent.walletScope.sessionData.getRememberedKey() === undefined; | |
if (!scope.requiresKey && scope.tx.type === TransactionType.Transfer && scope.tx.message && scope.tx.message.type === 2) { | |
var nisPort = scope.$parent.walletScope.nisPort; | |
var obj = {'params':{'address':scope.tx.recipient}}; | |
var _uriParser = document.createElement('a'); | |
_uriParser.href = scope.$parent.walletScope.sessionData.getNode().uri; | |
$http.get('http://'+_uriParser.hostname+':'+nisPort+'/account/get', obj).then(function (data){ | |
scope.recipientPublicKey = data.data.account.publicKey; | |
var privateKey = CryptoHelpers.decrypt(scope.$parent.walletScope.sessionData.getRememberedKey()); | |
var kp = KeyPair.create(privateKey); | |
if (kp.publicKey.toString() === scope.tx.signer) { | |
// sender | |
var privateKey = privateKey; | |
var publicKey = scope.recipientPublicKey; | |
} else { | |
var privateKey = privateKey; | |
var publicKey = scope.tx.signer; | |
} | |
var payload = scope.tx.message.payload; | |
scope.decoded = {'type':1, 'payload':CryptoHelpers.decode(privateKey, publicKey, payload) }; | |
scope.gettingRecipientInfo = false; | |
}, function(data) { | |
alert("couldn't obtain data from nis server"); | |
console.log("couldn't obtain data from nis server", scope.tx.recipient); | |
scope.gettingRecipientInfo = false; | |
}); | |
} | |
} | |
}; | |
}]); | |
mod.directive('taglevy', function(){ | |
return { | |
restrict: 'E', | |
scope: { | |
mos: '=', | |
tx: '=', | |
mosaics: '=' | |
}, | |
template: '', | |
transclude: true, | |
compile: function compile(tElement, tAttrs, transclude) { | |
return function postLink(scope, element, attrs) { | |
function mosaicIdToName(mosaicId) { | |
if (! mosaicId) return mosaicId; | |
return mosaicId.namespaceId + ":" + mosaicId.name; | |
} | |
function getLevy(d) { | |
if (!scope.mosaics) return undefined; | |
var mosaicName = mosaicIdToName(d.mosaicId); | |
if (!(mosaicName in scope.mosaics)) { | |
return undefined; | |
} | |
var mosaicDefinitionMetaDataPair = scope.mosaics[mosaicName]; | |
return mosaicDefinitionMetaDataPair.mosaicDefinition.levy; | |
} | |
scope.levy = getLevy(scope.mos); | |
var foo = scope; | |
scope.$watch('mosaics', function(nv, ov) { | |
scope.levy = getLevy(scope.mos); | |
//console.log('rerender', Object.keys(scope.mosaics).length, mosaicIdToName(scope.mos.mosaicId), mosaicIdToName(scope.levy ? scope.levy.mosaicId : undefined)); | |
}, true); | |
transclude(scope, function(clone, scope) { | |
element.append(clone); | |
}); | |
}; | |
} | |
}; | |
}); | |
mod.directive('title', function() { | |
return { | |
link: function($scope, element, attrs) { | |
var watch = $scope.$watch(function() { | |
return element.children().length; | |
}, function() { | |
$scope.$evalAsync(function() { | |
$('[data-toggle="tooltip"]').tooltip(); | |
}); | |
}); | |
}, | |
}; | |
}); | |
return mod; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment