interaction between the oracle smart contract and the oracle service - oracle

I want to use this code to recover the temperature and return the result to the smart contract
contract CMCOracle {
// Contract owner address public owner;
// BTC Marketcap Storage uint public btcMarketCap;
// Callback function event CallbackGetBTCCap();
function CMCOracle() public {
owner = msg.sender;
}
function updateWe() public {
// Calls the callback function
CallbackGetBTCCap();
}
function setBTCCap(uint cap) public {
// If it isn't sent by a trusted oracle
// a.k.a ourselves, ignore it
require(msg.sender == owner);
btcMarketCap = cap;
}
function getBTCCap() constant public returns (uint) {
return btcMarketCap;
}
}
var fetch = require('fetch')
var OracleContract = require('./build/contracts/CMCOracle.json')
var contract = require('truffle-contract')
var Web3 = require('web3');
var web3 = new Web3(new Web3.providers.HttpProvider('https://localhost:8545'));
// Truffle abstraction to interact with our
// deployed contract
var oracleContract = contract(OracleContract);
oracleContract.setProvider(web3.currentProvider);
// Dirty hack for web3#1.0.0 support for localhost testrpc
// see https://github.com/trufflesuite/truffle-contract/issues/56#issuecomment-331084530
if (typeof oracleContract.currentProvider.sendAsync !== "function") {
oracleContract.currentProvider.sendAsync = function() {
return oracleContract.currentProvider.send.apply(
oracleContract.currentProvider, arguments
);
};
}
// Get accounts from web3 web3.eth.getAccounts((err, accounts) => {
oracleContract.deployed().then((oracleInstance) => {
// Watch event and respond to event
// With a callback function
oracleInstance.CallbackGetBTCCap()
.watch((err, event) => {
// Fetch data
// and update it into the contract
fetch.fetchUrl('https://api.coinmarketcap.com/v1/global/',(err, m, b)=> {
const cmcJson = JSON.parse(b.toString());
const btcMarketCap = parseInt(cmcJson.total_market_cap_usd);
// Send data back contract on-chain
oracleInstance.setBTCCap(btcMarketCap, {from: accounts[0]});
})
})
}).catch((err) => { console.log(err) });
but I can't understand how to change the code.
How does the smart contract pass a city whose temperature I want to know to the oracle service?
What API does the oracle service use to fetch the temperature from the external source?
How should i change this code?
source: https://kndrck.co/posts/ethereum_oracles_a_simple_guide/

A smart contract does not interact with the API but the Oracle itself. Normally, it should have been two different contracts like one of the contracts should be separated from the external world. Oracle contract is the API for blockchain, which is basically residing in the blockchain. You can reach out to the contract by means of contract wrapper libraries (web3j, ethereumj)
The contract wrapper will fetch the data from API as JSON. And then the application will convert the data into primitive data that has been defined in the Solidity language. Once it is done, data will send via emit-event functions continuously to the blockchain as long as the application fetched data from the API. In the end, you will have a deterministic database source so that you can copy this source and transfer another place as it is.
For instance, you may change the API endpoint called "https://api.coinmarketcap.com/v1/global/" with "api.openweathermap.org/data/2.5/weather" and data structures (link: https://openweathermap.org/current).

Related

How to know which environment/network a NEAR smart-contract is deployed to (AssemblyScript)?

I'm doing some cross contract calls using NEAR and AssemblyScript. I would like to call different accounts based on the environment my smart-contract is deployed to. If the contract is deployed to testnet, I want to call a testnet cross-contract call. If the contract is deployed to mainnet, I want to call a mainnet cross-contract call.
export function callMetaNear(accountId: string): void {
// how to get correct contract name based on where the contract is deployed?
let otherContract: string = 'test.testnet';
if(contractIsDeployedToMainnet) {
otherContract = 'test.near';
}
// cross-contract call example
const itemArgs: AddItemArgs = {
accountId,
itemId: "Sword +9000",
};
const promise = ContractPromise.create(
otherContract,
"addItem",
itemArgs.encode(),
0,
);
promise.returnAsResult();
I will answer my own question, but I'm not sure if it's the best solution. Better answers are welcome.
I figured we can assume the contract is deployed to mainnet if Context.contractName ends with ".near".
import { Context } from 'near-sdk-core';
...
let otherContract: string = 'test.testnet';
if(Context.contractName.endsWith(".near")) {
otherContract = 'test.near';
}

Testing error for function call to a non-contract account

While testing our VRF getRandomNumber(s) with test-helpers, we keep on getting Error: Transaction reverted: function call to a non-contract account at:
require(LINK.balanceOf(address(this)) > fee, "Not enough LINK to initialte function call");
LINK seems to be used correctly here. What's the meaning/issue with the non-contract account?
Other tests on the same RandomNumberConsumer object are successful.
contract RandomNumberConsumer is VRFConsumerBase {
[...]
function getRandomNumber(uint256 userProvidedSeed) public returns (bytes32 requestId) {
require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet");
return requestRandomness(keyHash, fee, userProvidedSeed);
}
describe("getRandomNumber()", function() {
it("Should return a requestID", async function() {
const requestId = await randomNumberConsumer.getRandomNumber(12);
// checks on requestId
});
});
Any LINK.xxx() call refers to the external LINK contract which is not existent in your code. It's a contract already deployed on the network - that's why you're most probably passing the LINK address to the constructor of your contract.
To make it work in the test, you need to mock something so that your test doesn't end up calling the real LINK interface. One of the ways would be to mock your getRandomNumber function. Since it's public, that should be easily doable with Waffle's mocking utils: https://ethereum-waffle.readthedocs.io/en/latest/mock-contract.html.
Alternatively (probably more legit, but longer) you can mock the entire LINK contract:
Have some Mocks.sol contract:
pragma solidity ^0.8.7;
import "#chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";
abstract contract LinkMock is LinkTokenInterface {}
Initialize it as a mock in your test and pass its address to your mock contract as the first argument, as per the VRF documentation:
import { waffle } from 'hardhat'
import { abi as linkMockAbi } from '../artifacts/contracts/Mocks.sol/LinkMock.json'
const [deployer, vrfCoordinatorMock, ...actors] = waffle.provider.getWallets()
const getContract = async ({ mockedLinkBalance }: { mockedLinkBalance: string }) => {
const linkMockContract = await waffle.deployMockContract(deployer, linkMockAbi)
// Mocks the external LINK contract that we don't have access to during tests
await linkMockContract.mock.balanceOf.returns(ethers.utils.parseEther(mockedLinkBalance))
await linkMockContract.mock.transferAndCall.returns(true)
return waffle.deployContract(deployer, ContractJson, [
vrfCoordinatorMock.address,
linkMockContract.address,
'0x0000000000000000000000000000000000000000000000000000000000000000',
'100000000000000000',
])
}
Call rawFulfillRandomness() which the VRF calls itself, whenever you want to mock the VRF generating the randomness:
const contract = await getContract()
await contract.connect(vrfCoordinatorMock).rawFulfillRandomness('0x0000000000000000000000000000000000000000000000000000000000000000', mockedRandomnessValue)
Note I hardcoded requestId for brevity in the above example. You'll have to come up with a way of stubbing it if you rely on its value in your contract.

Luis Skill Recognizer Enumerations Error - Need to Update .luis & .qna files with changes

I have a virtual assistant dispatch that hands off control to a knowledge base skill that makes use of QnA maker. I will eventually have several QnA knowledgebases, but right now, I'm running into an issue getting the first one working. I've created/edited, trained, and publised/released the QnA maker KB, Knowledge Base Skill Luis model, and the Virtual Assistant's dispatch model.
Exception is being generated after the successful skill dispatch when my Knowledge Base Skill's Luis model returns the intent. I have a switch statement that will eventually point to the knowledge base corresponding to the user's question.
text: "Exception Message: Could not convert string 'helpdesk_faq' to dictionary key type
'Luis.KnowledgeBaseSkillLuis+Intent'. Create a TypeConverter to convert
from the string to the key type object.
I updated my KnowledgeBaseSkillLuis.cs intent enumeration with the name of the new intent (shown below), but I'm wondering if I didn't need to do this. I noticed my KnowledgeBaseSkill.luis and Faq.qna files do not have the updated changes; which leads me to my question -
How do I pull the updated models into my local environment? Do I need to run a botskills or dispatch command to pull the newly published intents into the code, or was I correct in manually updating the intent enumeration with my new skill? Do I need to re-publish the Assistant and/or Skill from my local machine to Azure to get them?
I've read these articles, but I'm struggling to make use of them:
Use Multiple LUIS and QnA Models
Add NLP to your Bot
Use QnA Maker to Answer Questions
// Full file included lower in the post
public enum Intent
{
Sample,
q_Faq,
helpdesk_faq, // Newly created intent (others were auto generated with Deploy scripts provided in skill template
None
};
MainDialog.cs:
...
switch (intent)
{
case KnowledgeBaseSkillLuis.Intent.Sample:
{
await innerDc.BeginDialogAsync(_sampleDialog.Id);
break;
}
case KnowledgeBaseSkillLuis.Intent.helpdesk_faq:
{
cognitiveModels.QnAServices.TryGetValue("Faq", out var qnaService); // "Faq" is the name of the QnA maker knowledge base.
if (qnaService == null)
{
await innerDc.Context.SendActivityAsync("I'm having issues looking up the information for you.");
throw new Exception("QnA Maker Service could not be found in the Bot Services Configuration.");
}
else
{
var answers = await qnaService.GetAnswersAsync(innerDc.Context, null, null);
if (answers != null && answers.Count() > 0)
{
await innerDc.Context.SendActivityAsync(answers[0].Answer);
}
else
{
await innerDc.Context.SendActivityAsync(_templateEngine.GenerateActivityForLocale("ConfusedMessage"));
}
}
break;
}
case KnowledgeBaseSkillLuis.Intent.None:
default:
{
// intent was identified but not yet implemented
await innerDc.Context.SendActivityAsync(_templateEngine.GenerateActivityForLocale("UnsupportedMessage"));
break;
}
}
...
KnowledgeBaseSkillLuis.cs:
public class KnowledgeBaseSkillLuis : IRecognizerConvert
{
public string Text;
public string AlteredText;
public enum Intent
{
Sample,
q_Faq,
helpdesk_faq,
None
};
public Dictionary<Intent, IntentScore> Intents;
public class _Entities
{
// Instance
public class _Instance
{
}
[JsonProperty("$instance")]
public _Instance _instance;
}
public _Entities Entities;
[JsonExtensionData(ReadData = true, WriteData = true)]
public IDictionary<string, object> Properties { get; set; }
public void Convert(dynamic result)
{
var app = JsonConvert.DeserializeObject<KnowledgeBaseSkillLuis>(JsonConvert.SerializeObject(result));
Text = app.Text;
AlteredText = app.AlteredText;
Intents = app.Intents;
Entities = app.Entities;
Properties = app.Properties;
}
public (Intent intent, double score) TopIntent()
{
Intent maxIntent = Intent.None;
var max = 0.0;
foreach (var entry in Intents)
{
if (entry.Value.Score > max)
{
maxIntent = entry.Key;
max = entry.Value.Score.Value;
}
}
return (maxIntent, max);
}
}
LUISGen is the tool that creates/updates the recognizer class (KnowledgeBaseSkillLuis) in your case.
How do I pull the updated models into my local environment? Do I need to run a botskills or dispatch command to pull the newly published intents into the code, or was I correct in manually updating the intent enumeration with my new skill?
You should use the update_cognitive_models.ps1 script (in Deployments\Scripts folder) with the RemoteToLocal switch. This will update from the online models to the local files.
Do I need to re-publish the Assistant and/or Skill from my local
machine to Azure to get them?
You should republish the new code (for the updated KnowledgeBaseSkillLuis) once it is updated with the script.
More info:
https://microsoft.github.io/botframework-solutions/virtual-assistant/handbook/deployment-scripts/#scripts
https://microsoft.github.io/botframework-solutions/virtual-assistant/handbook/devops/

how use coroutine to remodel / replace the callback implementation

Trying to replace the using callback with coroutines. Having a implementation using callback and not sure what is the right approach if coroutines could help.
This is the implementation with callback.
It has a class repository to provide data from either local database or network remote.
class Repository() {
var callback = //callback is provided by the caller
var isReady = false
var data = null
var firstimeCall = true //only need to get from database at first time call
fun getData(cb: ICallback) {
callback = cb
isReady = false
if (firstimeCall) {
firstimeCall = false
data = doGetDataFromDatabase() //sync call to get data from database
isReady = true
callback.onComplete(this)
}
isReady = false
data = doGetDataFromNetwork() {// async call and lamda as the callback
isReady = true
saveToDatabase(data)
callback.onComplete(this)
}
}
}
the repository.getData() could be called multiple times, only first time it will return the data from database first, then
getting from network and saving, then call callback.onComplete() to return the data.
Any other time, it will only do getting from network/save/return data through the callback.
the use case are:
directly using Repository, like
repository.getData() -- 1st time call
repository.getData() -- later call it again
there are multiple repositories, the data from each one will be aggregated into a final data.
for this case there is a Aggregator to hold the repositories, and provides onComplete() callback to process data if all
repositories are ready.
class Aggregator {
var repos = ArrayList<Repository>()
fun getData() {
for (r in repos) {
Thread {
r.getData(callback)
}.start()
}
}
fun processData(data: ArrayList()) {
......
}
val callback = object ICallback (onComplete{repo->
val hasAllComplete = repos.all {
it.isReady
}
if (hasAllComplete) {
var finalData = ArrayList<Data>()
for (r in repos) {
finalData.add(r.data)
}
processData(finalData)
}
})
}
so in the case it has two Repository, the Aggregator.getData() will get data from the two repositories.
when one Repository is complete its getData() call, it will callback to the callback's onComplete() where
the Aggregator will check wether all repositories are ready for data to be processed.
The same callback flow is used for the network call aswell.
Question:
In this case how to change to use coroutines, so that only after getting data from the database are complete for both repositories,
then it will start to get data from the network, without using callbacks.
I'm not sure if it's relevant anymore, but you could have a look at callbackFlow.
More info here:
https://medium.com/#elizarov/callbacks-and-kotlin-flows-2b53aa2525cf#1aaf
I have a similar problem, and I think this might be the solution to it.
Make sure you also read more about Flow and its usage before actually using it, since there are some caveats with handling exceptions (exception transparency), etc.

How to route ASP.Net Core api return value to appropriate observable based on data type returned

I have created an ASP.NET Core Web Api backend with an Angular 7 frontend. One of the methods in the Api can return either an object or an array to an Angular service. How do I route to specific observable, based on the data type returned? I am a noob to Angular, so any kind assistance would be appreciated.
Angular service call to Api:
getLinksFromSitus(situs: any) {
this.http.post(this.baseUrl + 'getLinksFromSitus', situs).subscribe(data =>
this.apiData.next(data)
);
}
Portion of Web Api that returns array if more than one APN present:
// if more than one item in list, get status information for each and return list to user to select appropriate apn
if (propApn.Count > 1)
{
return Ok(propApn);
}
Portion of same method to return object if only one value for APN:
var resultsModel = new Results
{
ArcGisLink = arcGisLink,
HistInfoLink = histInfoLink,
PropInfoLink = propInfoLink
};
return Ok(resultsModel);
You can't do this. Typescript can only type things based on static analysis at build time, what your describing would require Typescript to know the result of your API call at build time, which it doesn't do.
The best you can do is indicating that your API call can return both of your them:
public myApiFunc(req: MyRequestModel): Observable<any>
But that will still require you to figure out which type returned at runtime.
I was able to find a solution that worked...
getLinksFromSitus(situs: any) {
this.http.post(this.baseUrl + 'getLinksFromSitus', situs).subscribe(data => {
if (data.hasOwnProperty('arcGisLink')) {
this.apiData.next(data);
} else {
let vals = [];
vals = this.apiPropApn.getValue();
const item = vals.concat(data);
this.apiPropApn.next(item);
}
});
}
So, after subscribing to the HttpResponse, I am able to check if the data in the response contains a known property. If it doesn't contain the known property, then it concatenates the data to a BehaviorSubject array. It works perfectly.

Resources