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

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';
}

Related

How to encode arguments in AssemblyScript when calling Aurora contract from Near blockchain?

I'm trying to call a contract located in Aurora from a contract located in Near. I'm using AssemblyScript and I'm struggling with passing arguments to the Aurora contract itself. I receive ERR_BORSH_DESERIALIZE panic from the Aurora contract. Can anybody help me with figuring out how I would encode arguments? Here is sample code:
import { BorshSerializer } from '#serial-as/borsh'
#serializable
class FunctionCallArgs {
contract: Uint8Array;
input: Uint8Array;
}
export function myFunction(): void {
const args: FunctionCallArgs = {
contract: util.stringToBytes(contractAddress),
input: util.stringToBytes(abiEncodedFn),
};
const argsBorsh = BorshSerializer.encode(args);
ContractPromise.create("aurora", "call", argsBorsh, 100);
}
I managed to find a solution. The flow of calling the contract was right, however I had two errors in implementation.
Wrong conversion of the contract address to 20 byte array. My custom implementation of the function a bit verbose, so here is a single line JS script that does the same:
Buffer.from(contractAddress.substring(2), 'hex') // removing 0x prefix is mandatory
"#serial-as/borsh" doesn't deserialize fixed length arrays. So I had to convert contractAddress (which is Uint8Array after converting it to bytes in 1st point) to StaticArray(20), like this:
const contract = hexToBytes(tokenAddress).reduce((memo, v, i) => {
memo[i] = <u8>v;
return memo;
}, new StaticArray<u8>(20);
And finally monkey-patched "encode_static_array" method in the library to not allocate space before adding bytes to buffer. So removed:
encode_static_array<T>(value: StaticArray<T>): void {
...
this.buffer.store<u32>(value.length); // remove this line
...
}

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.

interaction between the oracle smart contract and the oracle service

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).

Microsoft teams bot getting members of a channel

in our use case, we have the team channel id with me. This is captured and stored into the DB in the event below
this.onTeamsChannelCreatedEvent(async (channelInfo: any, teamInfo: any, turnContext: any, next: () => Promise<void>): Promise<void> => {
}
We are running a periodic job and need to find out the members of a channel to perform some operations. So, how can i find members info from the channelid?
I have seen examples which using the context or context.activity. But in this case context wont be available. we are using nodejs v4.
My code here is
var client = new ConnectorClient(credentials, { baseUri: channel.serviceUrl });
client.fetchMembers(channel.serviceUrl, channel.channelId,
(err, result) => {
if (err) {
functions.logger.log("failed to fetch team members")
}
else {
functions.logger.log("Team members are", result)
}
}
);
This now throws error
TypeError: client.fetchMembers is not a function
i have also tried client.fetchMembers but similar error.
There are two main ways you can do this:
Using the Microsoft Graph, in particular see the List Members operation.
By having a bot registered in the channel, which it sounds like you have already. You don't need an 'activity' at all, you can do this any time by accessing the Conversations object. In fact, this code doesn't even need to run in your "bot" project at all - as long as the bot is added to the team/channel, it will work to use the bot's credentials. You can see sample code here in the Microsoft docs, and I have a sample from a recent conference session, doing this in dotnet from a console app over here.
One thing that's worth noting, the members of a channel and the members of a Team are largely the same (only excluding Private channels), so that's why some of these options refer to the Team or Group.
You'll want to use teamsInfo.getTeamMembers() or teamsInfo.getPagedMembers() (if you want a paginated list), as in this sample:
async getPagedMembers(context) {
var continuationToken;
var members = [];
do {
var pagedMembers = await TeamsInfo.getPagedMembers(context, 100, continuationToken);
continuationToken = pagedMembers.continuationToken;
members.push(...pagedMembers.members);
} while (continuationToken !== undefined);
return members;
}

Single AWS Lambda function to respond to Alexa skill requests, and return a JSON object, depending on how it is called

I am trying to use the same AWS Lambda function to do two things with the same DynamoDB dataset.
(a) Provide Alexa Skill Responses
I have already implemented this, and the Skill is operating correctly. I am using NodeJS and the Amazon Skills Kit version 2. The last few lines of my index.js are as follows:
const skillBuilder = Alexa.SkillBuilders.standard();
exports.handler = skillBuilder
.addRequestHandlers(
LaunchRequest,
HelpIntent,
// .... various other intents
UnhandledIntent
)
.addErrorHandlers(ErrorHandler)
.lambda();
(b) Provide a JSON object summarising some database contents
The NodeJS code I have set up for the Alexa skill, processes DynamoDB data in order to provide the skill responses. I want to use the same codebase (i.e. the same Lambda function) to produce summaries for my own benefit. I don't want to copy-paste pieces of the Lambda function into a separate function. I would much rather have the same code do both tasks, in order to keep everything in step.
My problem is that the structure of the Lambda code for returning this JSON response to my request is as follows, from https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html:
'use strict';
console.log('Loading hello world function');
exports.handler = function(event, context, callback) {
let name = "you";
let city = 'World';
// etc ... more code ...
callback(null, response);
};
Both pieces of code assign a function to `exports.handler`
I think I want to achieve this effect:
if ( /* test whether being called by Alexa or by API Gateway */ )
{ /* Make the Alexa Response */ }
else
{ /* Construct the JSON data summary response */ }
From what I can make out, each Lambda function (in the sense of each Amazon Resource Number for a Lambda function) has to have only one entry file, i.e. I can't make Lambda start index_Alexa.js versus index_JSON.js.
I am open to any suggestion on how to get the same Lambda function (in the sense of the same JS file or package of files) do both things.
I question the usefulness of the approach somewhat, and the following has some potential for optimization remaining, but one way of accomplishing this is to declare exports.handler as a simple wrapper that invokes the correct previously-declared handler function based on a condition you can test in the request.
// set up handler for alexa
const skillBuilder = Alexa.SkillBuilders.standard();
const alexa_handler = skillBuilder
.addRequestHandlers(
LaunchRequest,
HelpIntent,
// .... various other intents
UnhandledIntent
)
.addErrorHandlers(ErrorHandler)
.lambda();
// set up handler for API Gateway
const api_gateway_handler = function(event, context, callback) {
let name = "you";
let city = 'World';
// etc ... more code ...
callback(null, response);
};
// for each invocation, choose which of the above to invoke
exports.handler = function(event, context, callback) {
if(/* some test that is true only for API Gateway */)
{
api_gateway_handler(event, context, callback);
}
else
{
alexa_handler(event, context, callback);
}
};
The line if(/* some test that is true only for API Gateway */) is something you'll need to work out, but I suspect something like this might work:
if(event.input && event.input.requestContext && event.input.requestContext.apiId)
The API Gateway docs suggest that this value would always be present.

Resources