waitOnReceipt function doesn't work in solidity, running on SuperBlocks - ethereum

Running on https://studio.superblocks.com/ trying to make a a fun game in Solidity. the problem happens when I call waitfor receipt inside the "addKity" function. Any ideas why? I suspect it could be because of a technical issue in superblocks but could very well be because of a coding error I have.
It reaches the "heres" alert but doesn't reach alert("4");
Please let me know your thoughts!
waitForReceipt(txHash, function(receipt){
Javascript File
(function (Contract) {
var web3_instance;
var instance;
var accounts;
function init(cb) {
web3_instance = new Web3(
(window.web3 && window.web3.currentProvider) ||
new Web3.providers.HttpProvider(Contract.endpoint));
accounts = web3.eth.accounts;
var contract_interface = web3_instance.eth.contract(Contract.abi);
instance = contract_interface.at(Contract.address);
cb();
}
function waitForReceipt(hash, cb) {
web3.eth.getTransactionReceipt(hash, function (err, receipt) {
if (err) {
error(err);
}
if (receipt !== null) {
// Transaction went through
if (cb) {
cb(receipt);
}
} else {
// Try again in 1 second
window.setTimeout(function () {
waitForReceipt(hash, cb);
}, 1000);
}
});
}
function addKitty(){
// var KittyName = "he"; //$("#kittyName").val();
// var KittyPrice = 5;//parseInt($("#kittyPrice").val());
const transactionObject = {
from: "0x95c2332b26bb22153a689ae619d81a6c59e0a804",
gas: "1000000",
gasPrice: "1000000",
value:"1000"
};
instance.addNewKitty.sendTransaction("sdad",5, transactionObject, (error, result) => {
if(error){
alert(error);
alert("2");
}
else{
alert("heres");
waitForReceipt(txHash, function(receipt){
alert("4");
// if(receipt.status === "0x1"){
// alert("got receipt");
// }
// else{
// alert("5");
// alert("receipt status fail");
// }
});
}
})
}
// function getMessage(cb) {
// instance.message(function (error, result) {
// cb(error, result);
// });
// }
$(document).ready(function () {
init(function () {
// getMessage(function (error, result) {
// if (error) {
// console.error("Could not get article:", error);
// return;
// }
// $('#message').append(result);
// });
$("#addKitty").click(function(){
addKitty();
})
});
});
})(Contracts['CryptoKitties']);
Solidity file
pragma solidity ^0.4.21;
contract CryptoKitties {
address public owner;
struct CKitties{
string name;
uint price;
}
function CryptoKitties() public {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
mapping (address => uint) kittytoUser;
CKitties[] kitties;
event NewCryptoKitty(address owner, string name, uint price);
modifier cost (uint minCost){
require(msg.value>= minCost && minCost >=0);
_;
}
function addNewKitty(string newName, uint newPrice) public payable cost(5000) {
address sender = msg.sender;
uint place = kitties.push(CKitties(newName,newPrice));
kittytoUser[sender] = place;
emit NewCryptoKitty(sender, newName, newPrice);
}
function kill() public onlyOwner{
selfdestruct(owner);
}
function getName() public view returns (string){
address sender = msg.sender;
uint index = kittytoUser[sender]-1;
return kitties[index].name;
}
function setName(string newName) public{
address sender = msg.sender;
uint index = kittytoUser[sender]-1;
kitties[index].name = newName;
}
function getBalance() public view returns (uint){
return address(this).balance;
}
}

First, the waitForReceipt function is getting called passing an undefined value txHash. In that context, it should be result instead (the value coming from the callback that activated that call, triggered by sendTransaction).
That change will make waitForReceipt call work.
The other problem is that you have the web3 variable named as web3_instance, but you still refer to it as web3.
Considering that, inside waitForReceipt, you are still referencing to it as web3.eth..., while the correct call in that context would be: web3_instance.eth.getTransactionReceipt....
With that fixed, alert("4") will get called as intended.

Related

Unable to initialize OpenZeppelin Clone contract during Hardhat Test (Error: VM Exception)

I'm trying to create clones of my implementation contract (EIP-1167) using the OpenZeppelin Clones library, however I keep getting a VM Exception Error.
This is the 'Initialize' function of my Implementation contract:
contract Whoopy is Initializable, VRFConsumerBaseV2, KeeperCompatible {
function initialize(address _whoopyCreator) public initializer {
VRFConsumerBaseV2.initialise(vrfCoordinatorV2);
i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2);
s_raffleState = RaffleState.CLOSED;
s_lastTimeStamp = block.timestamp;
whoopyCreator = _whoopyCreator;
i_vrfCoordinator.addConsumer(subscriptionId, address(this));
emit NewConsumerAdded(address(this));
}
This is my CloneFactory:
pragma solidity ^0.8.8;
import "#openzeppelin/contracts/proxy/Clones.sol";
contract WhoopyFactory {
address public implementationContract;
address[] public allClones;
event NewClone(address indexed _instance);
mapping(address => address) public whoopyList;
constructor(address _implementation) {
implementationContract = _implementation;
}
function createClone(address _whoopyCreator) payable external returns (address instance) {
instance = Clones.clone(implementationContract);
(bool success, ) = instance.call{value: msg.value}(abi.encodeWithSignature("initialize(address)", _whoopyCreator));
require(success==true, "initialize did not return true");
allClones.push(instance);
whoopyList[_whoopyCreator] = instance;
emit NewClone(instance);
return instance;
}
This is the test I am running:
const { assert, expect } = require("chai")
const { ethers, upgrades } = require("hardhat")
const { networkConfig } = require("../../helper-hardhat-config")
const fs = require("fs")
const path = require("path")
const { web3, Contract } = require("web3")
const getGas = async (tx) => {
const receipt = await ethers.provider.getTransactionReceipt(tx.hash)
return receipt.gasUsed.toString()
}
let whoopy,
Whoopy,
WhoopyFactory,
wf,
whoopyStandaloneGas,
whoopyFactoryGas,
clonedContract,
accountConnectedClone,
wCreator,
wallet,
addr1,
addr2
describe("Whoopy + WhoopyFactory", function () {
it("Initialises contract correctly", async function () {
provider = ethers.provider
addr1 = new ethers.Wallet("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", provider)
addr2 = new ethers.Wallet("0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", provider)
Whoopy = await ethers.getContractFactory("Whoopy", addr1)
whoopy = await Whoopy.deploy()
await whoopy.deployed()
const dir = path.resolve(
__dirname,
"/Users/boss/hardhat-smartcontract-lottery/artifacts/contracts/Whoopy.sol/Whoopy.json"
)
const file = fs.readFileSync(dir, "utf8")
const json = JSON.parse(file)
const abi = json.abi
WhoopyFactory = await ethers.getContractFactory("WhoopyFactory", addr1)
wf = await WhoopyFactory.deploy(whoopy.address)
await wf.deployed()
wf.connect(addr2)
const tx = await wf.createClone("0x70997970C51812dc3A010C7d01b50e0d17dc79C8", {
value: ethers.utils.parseUnits("1", "ether"),
})
console.log(tx)
const txReceipt = await tx.wait(1)
console.log(txReceipt)
})
})
When I run this test, I get the following error:
Error: VM Exception while processing transaction: reverted with reason string 'initialize did not return true'
This exact same code works perfectly when I try it in Remix, so I assume the issue has something to do with Hardhat.
Does anyone have any idea where the issue is? I've been racking my head for days but can't seem to figure out. I'd be extremely grateful if someone can point me in the right direction.
Thanks!
NOTE: Here is my hardhat config (in case it makes a difference):
require("#nomiclabs/hardhat-waffle")
require("#nomiclabs/hardhat-etherscan")
require("hardhat-deploy")
require("solidity-coverage")
require("hardhat-gas-reporter")
require("hardhat-contract-sizer")
require("dotenv").config()
require("#nomiclabs/hardhat-ethers");
// require('#openzeppelin/contracts');
const RINKEBY_RPC_URL = process.env.RINKEBY_RPC_URL
const PRIVATE_KEY = process.env.PRIVATE_KEY
const COINMARKETCAP_API_KEY = process.env.COINMARKETCAP_API_KEY
const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY
/** #type import('hardhat/config').HardhatUserConfig */
module.exports = {
defaultNetwork: "hardhat",
networks: {
hardhat: {
chainId: 31337,
blockConfirmations: 1
},
localhost: {
chainId: 31337,
},
rinkeby: {
chainId: 4,
blockConfirmations: 6,
url: RINKEBY_RPC_URL,
accounts: [PRIVATE_KEY]
}
},
solidity: "0.8.8",
gasReporter: {
enabled: true,
currency: "USD",
outputFile: "gas-report.txt",
noColors: true,
// coinmarketcap: process.env.COINMARKETCAP_API_KEY,
},
namedAccounts: {
deployer: {
default: 0,
},
player: {
default: 1,
}
},
mocha: {
timeout: 900000,
},
etherscan: {
apiKey: {
rinkeby: ETHERSCAN_API_KEY,
// kovan: ETHERSCAN_API_KEY,
// polygon: POLYGONSCAN_API_KEY,
},
},
};

I am stuck with Promise { <pending> } in helper function in ejs file

Below is my helper function
async function getPriorityList(operatorId) {
let result = await Api.getApisDetail(operatorId);
return result;
};
and below is my modal function
static getApisDetail(operatorId) {
return db.query(
`select id, api_name, api_type,(select priority from api_switching where api_switching.api_id=api_master.id and operator_id=${operatorId}) as priority,(select isActive from api_switching where api_switching.api_id=api_master.id and operator_id=${operatorId}) as isActive from api_master where api_type='Recharge' order by api_name asc`
);
}

How to get Email of the user who performed an activity in Google Drive?

I am trying to get data from Drive Activity API. The data needs to have the following 4 arguments:
User Information
Filename
Activity Type (Edit, Delete, Copy)
Timestamp
I used the following code to get the required data:
function listDriveActivity() {
var arr = [];
const request = {
pageSize: 10
};
try {
const response = DriveActivity.Activity.query(request);
const activities = response.activities;
if (!activities || activities.length === 0) {
Logger.log('No activity.');
return;
}
for (const activity of activities) {
// get time information of activity.
var time = getTimeInfo(activity);
// get the action details/information
var action = getActionInfo(activity.primaryActionDetail);
// get the actor's details of activity
var actors = activity.actors.map(getActorInfo);
// get target information of activity.
var targets = activity.targets.map(getTargetInfo);
arr.push(actors,targets,[action],[time]);
}
Logger.log(arr);
} catch (err) {
Logger.log('Failed with an error %s', err.message);
}
}
function getOneOf(object) {
for (const key in object) {
return key;
}
return 'unknown';
}
function getTimeInfo(activity) {
if ('timestamp' in activity) {
return activity.timestamp;
}
if ('timeRange' in activity) {
return activity.timeRange.endTime;
}
return 'unknown';
}
function getActionInfo(actionDetail) {
return getOneOf(actionDetail);
}
function getUserInfo(user) {
if ('knownUser' in user) {
const knownUser = user.knownUser;
const isMe = knownUser.isCurrentUser || false;
return isMe ? 'people/me' : knownUser.personName;
}
return getOneOf(user);
}
function getActorInfo(actor) {
if ('user' in actor) {
return getUserInfo(actor.user);
}
return getOneOf(actor);
}
function getTargetInfo(target) {
if ('driveItem' in target) {
const title = target.driveItem.title || 'unknown';
return 'driveItem:"' + title + '"';
}
if ('drive' in target) {
const title = target.drive.title || 'unknown';
return 'drive:"' + title + '"';
}
if ('fileComment' in target) {
const parent = target.fileComment.parent || {};
const title = parent.title || 'unknown';
return 'fileComment:"' + title + '"';
}
return getOneOf(target) + ':unknown';
}
This is the example output, when there is some activity in the drive by a user:
User Information: people/107464693787053536449
Filename: Timesheet
Activity Type: Edit
Timestamp: 2022-04-05T04:51:41.862Z
Now, I want to get the user email in User information rather than user id. Can you please guide me how should I do it? Is there any method/function that I could follow?
You have to use People API if you want to get more information about the user, including the email address:
personName: The identifier for this user that can be used with the People API to get more information. The format is people/ACCOUNT_ID. See https://developers.google.com/people/.
More specifically, call people.get with personFields set to emailAddresses:
function getEmailAddress(resourceName = "people/ACCOUNT_ID") {
const optionalArgs = {
personFields: "emailAddresses",
fields: "emailAddresses(value)"
}
const contact = People.People.get(resourceName, optionalArgs);
const emailAddress = contact.emailAddresses[0].value;
return emailAddress;
}

Simple Open Auction example contract in Solidity docs doesn't pay beneficiary

I tried the Simple Open Auction example (https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#simple-open-auction) from the Solidity docs with ganache-cli and truffle. In migrations/2_deploy_contracts.js I set up:
var SimpleAuction = artifacts.require("./SimpleAuction.sol");
module.exports = function(deployer) {
deployer.deploy(SimpleAuction, 300, "0xe6ebc74aa685527a83c9e0df01b21acf0a1e8286");
};
With "0xe6ebc74aa685527a83c9e0df01b21acf0a1e8286" the address of account 1 in ganache.
In truffle console, I executed the following commands to simulate an auction (and I waited 300 seconds before calling auction.auctionEnd(), which succeeded):
auction = SimpleAuction.at(SimpleAuction.address)
account1 = web3.eth.accounts[1]
account2 = web3.eth.accounts[2]
account3 = web3.eth.accounts[3]
account4 = web3.eth.accounts[4]
auction.bid({from: account2, value: web3.toWei(10, "ether")})
auction.bid({from: account3, value: web3.toWei(13, "ether")})
auction.bid({from: account4, value: web3.toWei(15, "ether")})
auction.withdraw({from: account2})
auction.withdraw({from: account3})
auction.auctionEnd()
web3.fromWei(web3.eth.getBalance(account1).toString(), "ether")
web3.fromWei(web3.eth.getBalance(account2).toString(), "ether")
web3.fromWei(web3.eth.getBalance(account3).toString(), "ether")
web3.fromWei(web3.eth.getBalance(account4).toString(), "ether")
After this, the balances are:
100
99.9936296
99.9945537
84.9945537
Account 4 has won the auction and paid 15 eth, but I expected account 1 to have a balance of 115 eth, because this account is the beneficiary. I suppose the example code, which I copied verbatim, doesn't have a bug, so what am I doing wrong here?
The contract code is:
pragma solidity ^0.4.22;
contract SimpleAuction {
// Parameters of the auction. Times are either
// absolute unix timestamps (seconds since 1970-01-01)
// or time periods in seconds.
address public beneficiary;
uint public auctionEnd;
// Current state of the auction.
address public highestBidder;
uint public highestBid;
// Allowed withdrawals of previous bids
mapping(address => uint) pendingReturns;
// Set to true at the end, disallows any change
bool ended;
// Events that will be fired on changes.
event HighestBidIncreased(address bidder, uint amount);
event AuctionEnded(address winner, uint amount);
// The following is a so-called natspec comment,
// recognizable by the three slashes.
// It will be shown when the user is asked to
// confirm a transaction.
/// Create a simple auction with `_biddingTime`
/// seconds bidding time on behalf of the
/// beneficiary address `_beneficiary`.
constructor(
uint _biddingTime,
address _beneficiary
) public {
beneficiary = _beneficiary;
auctionEnd = now + _biddingTime;
}
/// Bid on the auction with the value sent
/// together with this transaction.
/// The value will only be refunded if the
/// auction is not won.
function bid() public payable {
// No arguments are necessary, all
// information is already part of
// the transaction. The keyword payable
// is required for the function to
// be able to receive Ether.
// Revert the call if the bidding
// period is over.
require(
now <= auctionEnd,
"Auction already ended."
);
// If the bid is not higher, send the
// money back.
require(
msg.value > highestBid,
"There already is a higher bid."
);
if (highestBid != 0) {
// Sending back the money by simply using
// highestBidder.send(highestBid) is a security risk
// because it could execute an untrusted contract.
// It is always safer to let the recipients
// withdraw their money themselves.
pendingReturns[highestBidder] += highestBid;
}
highestBidder = msg.sender;
highestBid = msg.value;
emit HighestBidIncreased(msg.sender, msg.value);
}
/// Withdraw a bid that was overbid.
function withdraw() public returns (bool) {
uint amount = pendingReturns[msg.sender];
if (amount > 0) {
// It is important to set this to zero because the recipient
// can call this function again as part of the receiving call
// before `send` returns.
pendingReturns[msg.sender] = 0;
if (!msg.sender.send(amount)) {
// No need to call throw here, just reset the amount owing
pendingReturns[msg.sender] = amount;
return false;
}
}
return true;
}
/// End the auction and send the highest bid
/// to the beneficiary.
function auctionEnd() public {
// It is a good guideline to structure functions that interact
// with other contracts (i.e. they call functions or send Ether)
// into three phases:
// 1. checking conditions
// 2. performing actions (potentially changing conditions)
// 3. interacting with other contracts
// If these phases are mixed up, the other contract could call
// back into the current contract and modify the state or cause
// effects (ether payout) to be performed multiple times.
// If functions called internally include interaction with external
// contracts, they also have to be considered interaction with
// external contracts.
// 1. Conditions
require(now >= auctionEnd, "Auction not yet ended.");
require(!ended, "auctionEnd has already been called.");
// 2. Effects
ended = true;
emit AuctionEnded(highestBidder, highestBid);
// 3. Interaction
beneficiary.transfer(highestBid);
}
}
When I call auction.auctionEnd() before the 300 seconds, I get an exception "Auction not yet ended", which is what it is supposed to do:
truffle(development)> auction.auctionEnd()
Error: VM Exception while processing transaction: revert Auction not yet ended.
at XMLHttpRequest._onHttpResponseEnd (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:509:1)
at XMLHttpRequest._setReadyState (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:354:1)
at XMLHttpRequestEventTarget.dispatchEvent (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:64:1)
at XMLHttpRequest.request.onreadystatechange (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/httpprovider.js:128:1)
at /usr/local/lib/node_modules/truffle/build/webpack:/packages/truffle-provider/wrapper.js:134:1
at /usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/requestmanager.js:86:1
at Object.InvalidResponse (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/errors.js:38:1)
When I call it after 300 seconds, I get a transaction and an event AuctionEnded, so it looks like the now >= auctionEnd condition is met:
truffle(development)> auction.auctionEnd()
{ tx: '0x480208cd6c4ac3580e7dcc3aa7e64cd0e7b5e11d5bea75e4769b554767158e35',
receipt:
{ transactionHash: '0x480208cd6c4ac3580e7dcc3aa7e64cd0e7b5e11d5bea75e4769b554767158e35',
transactionIndex: 0,
blockHash: '0xfde6956d9c7d6e99235606b70e11161965ddf832063e572a87df00c14484e5a1',
blockNumber: 12,
gasUsed: 76921,
cumulativeGasUsed: 76921,
contractAddress: null,
logs: [ [Object] ],
status: '0x1',
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000100000000040000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' },
logs:
[ { logIndex: 0,
transactionIndex: 0,
transactionHash: '0x480208cd6c4ac3580e7dcc3aa7e64cd0e7b5e11d5bea75e4769b554767158e35',
blockHash: '0xfde6956d9c7d6e99235606b70e11161965ddf832063e572a87df00c14484e5a1',
blockNumber: 12,
address: '0xef0a6e95779240516e8a05039f97968f727c4f18',
type: 'mined',
event: 'AuctionEnded',
args: [Object] } ] }
Hopefully this can help you. I think you missed time travel part.
// test/SimpleAuction.js
const sa = artifacts.require("SimpleAuction");
contract('SimpleAuction', async function(accounts) {
/**
* TimeTravel function - can move this to helper file
* https://www.reddit.com/r/ethdev/comments/6n65ar/using_testrpc_and_truffles_built_in_js_tests_how/dk7357l/
*/
const timeTravel = function (time) {
return new Promise((resolve, reject) => {
web3.currentProvider.sendAsync({
jsonrpc: "2.0",
method: "evm_increaseTime",
params: [time], // 86400 is num seconds in day
id: new Date().getTime()
}, (err, result) => {
if(err){ return reject(err) }
return resolve(result)
});
})
}
var sa_instance;
var highest_bid = 2; // ether
var bob = accounts[1];
var alice = accounts[2];
var john = accounts[3];
var beneficiary = accounts[accounts.length - 1]; // last account in Ganache
var beneficiary_initial_balance;
before(async function() {
sa_instance = await sa.new(300, beneficiary);
var bib_balance_wei = await web3.eth.getBalance(beneficiary);
var bib_balance_eth = web3.fromWei(web3.toDecimal(bib_balance_wei), "ether");
beneficiary_initial_balance = parseInt(bib_balance_eth);
})
it("Bob bids with 1 ETH", async function() {
await sa_instance.bid({from: bob, value: web3.toWei(1, "ether")})
var sa_balance_wei = await web3.eth.getBalance(sa_instance.address);
var sa_balance_eth = web3.fromWei(web3.toDecimal(sa_balance_wei), "ether");
assert.equal(sa_balance_eth, 1);
})
it("Alice bids with 2 ETH", async function() {
await sa_instance.bid({from: alice, value: web3.toWei(highest_bid, "ether")})
var sa_balance_wei = await web3.eth.getBalance(sa_instance.address);
var sa_balance_eth = web3.fromWei(web3.toDecimal(sa_balance_wei), "ether");
assert.equal(sa_balance_eth, 3);
})
it("Bob can withdraw his 1 ETH back", async function() {
await sa_instance.withdraw({from: bob})
var sa_balance_wei = await web3.eth.getBalance(sa_instance.address);
var sa_balance_eth = web3.fromWei(web3.toDecimal(sa_balance_wei), "ether");
assert.equal(sa_balance_eth, highest_bid);
})
it("Anyone can end the auction when it's due", async function() {
await timeTravel(300);
await sa_instance.auctionEnd({from: john});
})
it("Contract balance left with 0 ETH", async function() {
var sa_balance_wei = await web3.eth.getBalance(sa_instance.address);
var sa_balance_eth = web3.fromWei(web3.toDecimal(sa_balance_wei), "ether");
assert.equal(sa_balance_eth, 0);
})
it("And beneficiary is now 2 ETH richer", async function() {
var bib_balance_wei = await web3.eth.getBalance(beneficiary);
var bib_balance_eth = web3.fromWei(web3.toDecimal(bib_balance_wei), "ether");
var expected_new_beneficiary_balance = beneficiary_initial_balance + highest_bid;
assert.equal(expected_new_beneficiary_balance, bib_balance_eth);
})
})
Did auctionEnd() succeed?
Most likely require(now >= auctionEnd) is not satisfied thus auctionEnd() failed.
Update: Debugged the contract myself, the transfer is successful. Beneficiary address is: 0xe6ebc74aa685527a83c9e0df01b21acf0a1e8286 not account1. So the contract works as expected, there is no problem. Check the balance of beneficiary address, you will see it has 15 ether.
Clarification:
This is the deployment instruction:
module.exports = function(deployer) {
deployer.deploy(SimpleAuction, 300, "0xe6ebc74aa685527a83c9e0df01b21acf0a1e8286");
};
So you set beneficiary address as: 0xe6ebc74aa685527a83c9e0df01b21acf0a1e8286.
And this is the transfer instruction inside auctionEnd():
beneficiary.transfer(highestBid);
So after the execution, if you run the following command you will see that beneficiary has 15 ether.
web3.eth.getBalance("0xe6ebc74aa685527a83c9e0df01b21acf0a1e8286")
BigNumber { s: 1, e: 19, c: [ 150000 ] }

Setting data in viewModel knockoutjs from html5 websocket

I am trying to create knockout.js component that is getting data from HTML5 Websocket. Websocket code is in separate script e.g. util.js. I am able to connect and get data from socket, but dont know how correctly to set corresponding property in component`s ViewModel.
Websocket - util.js:
var options = {
server: '127.0.0.1',
port: '12345'
};
var socket, loadedFlag;
var timeout = 2000;
var clearTimer = -1;
var data = {};
function handleErrors(sError, sURL, iLine)
{
return true;
};
function getSocketState()
{
return (socket != null) ? socket.readyState : 0;
}
function onMessage(e)
{
data=$.parseJSON(e.data);
// ???? Is it possible to have here something like
// ???? viewModel.getDataWS1(data);
}
function onError()
{
clearInterval(clearTimer);
socket.onclose = function () {
loadedFlag = false;
};
clearTimer = setInterval("connectWebSocket()", timeout);
}
function onClose()
{
loadedFlag = false;
clearInterval(clearTimer);
clearTimer = setInterval("connectWebSocket()", timeout);
}
function onOpen()
{
clearInterval(clearTimer);
console.log("open" + getSocketState());
}
function connectWebSocket()
{
if ("WebSocket" in window)
{
if (getSocketState() === 1)
{
socket.onopen = onOpen;
clearInterval(clearTimer);
console.log(getSocketState());
}
else
{
try
{
host = "ws://" + options.server + ":" + options.port;
socket = new WebSocket(host);
socket.onopen = onOpen;
socket.onmessage = function (e) {
onMessage(e);
};
socket.onerror = onError;
socket.onclose = onClose;
}
catch (exeption)
{
console.log(exeption);
}
}
}
}
Component (productDisplay.js) - creating so that is can be used on multiple pages:
define([
'jquery',
'app/models/productDisplayModel',
'knockout',
'mapping',
'socket'
],
function ($, model, ko, mapping) {
ko.components.register('product', {
viewModel: {require: 'app/models/productModel'},
template: {require: 'text!app/views/product.html'}
});
});
Product ViewModel (productModel.js) - where I struggle to set viewModel property to data from websocket:
var viewModel = {};
define(['knockout', 'mapping', 'jquery'], function (ko, mapping, $) {
function Product(name, rating) {
this.name = name;
this.userRating = ko.observable(rating || null);
}
function MyViewModel() {
this.products = ko.observableArray(); // Start empty
}
MyViewModel.prototype.getDataWS1 = function () {
//Websocket has not connected and returned data yet, so data object is empty
// ???? Is there anyway I can add something like promise so that the value is set once socket is connected?
this.products(data);
};
// apply binding on page load
$(document).ready(function () {
connectToServer1();
viewModel = new MyViewModel();
ko.applyBindings(viewModel);
viewModel.getDataWS1();
});
});
Thank you for any ideas.
You can update an observable when you get a message in the following manner:
util.js
function onMessage(e) {
var productData = $.parseJSON(e.data);
viewModel.addNewProduct(productData);
}
productModel.js
function Product(name, rating) {
this.name = name;
this.userRating = ko.observable(rating || null);
}
function MyViewModel() {
this.products = ko.observableArray(); // Start empty
}
MyViewModel.prototype.addNewProduct(product) {
var newProduct = new Product(product.name, product.rating);
this.products.push(newProduct);
}
Basically the idea is that when you get a message (in onMessage function), you will parse the data and call a function in your viewmodel to add the message data to the viewmodel properties (observables, observableArrays, etc.)