I have a smart contract for sending money user-to-user.
In this smart contract, I pass the smart contract address from the Kovan network when the user selects a coin.
In this case, I pass the ChainLink contract address to my contract for sending the Chain Link token to the user.
This is ChainLink contract address in the Kovan network: 0xa36085f69e2889c224210f603d836748e7dc0088.
Now I want to test contract functions with this code:
const assert = require("assert");
const Dex = artifacts.require("Dex");
contract("Dex", (accounts) => {
let dex;
let contractOwner = null;
let buyer = null;
beforeEach(async () => {
contractOwner = accounts[0];
buyer = accounts[1];
dex = await Dex.new();
contractOwner = accounts[0];
// sometimes you need to test a user except contract owner
buyer = accounts[1];
});
// it("Add tokens and contract address Success", async () => {
// const tokenInfo = await dex.getTokenContractAddress("USDT");
// assert(tokenInfo == "0xdac17f958d2ee523a2206206994597c13d831ec7");
// })
// it("Add Token", async () => {
// await dex.addToken(web3.utils.fromAscii("LINK"), "0xa36085f69e2889c224210f603d836748e7dc0088", "0xa36085f69e2889c224210f603d836748e7dc0088")
// })
it("Deposit", async () => {
await dex.deposit("0xa36085f69e2889c224210f603d836748e7dc0088", "0x5226a51522C23CcBEFd04a2d4C6c8e281eD1d680", "0xB643992c9fBcb1Cb06b6C9eb278b2ac35e6a2711", 1,
// you were missing this
{ from: accounts[0] });
})
})
I Using Truffle in Project:
Truffle Config:
const HDWalletProvider = require('#truffle/hdwallet-provider');
const fs = require('fs');
const mnemonic = fs.readFileSync(".secret").toString().trim();
const secrets = JSON.parse(
fs.readFileSync('.secrets.json').toString().trim()
);
module.exports = {
networks: {
kovan: {
networkCheckTimeout: 10000,
provider: () => {
return new HDWalletProvider(
mnemonic,
`https://kovan.infura.io/v3/1461728202954c07bd5ed5308641a054`,
0,
20
);
},
network_id: "42",
},
},
// Set default mocha options here, use special reporters, etc.
mocha: {
},
// Configure your compilers
compilers: {
solc: {
version: "0.8.10",
docker: false,
settings: {
optimizer: {
enabled: false,
runs: 200
},
evmVersion: "byzantium"
}
}
},
};
My Contract:
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
import "#openzeppelin/contracts/token/ERC20/IERC20.sol";
// import "./UserCoins.sol";
contract Dex {
event Approval(
address indexed tokenOwner,
address indexed spender,
uint256 tokens
);
event Transfer(address indexed from, address indexed to, uint256 tokens);
constructor() {}
function deposit(
address ticker,
address sender,
address recipient,
uint256 amount
) external payable {
IERC20 token = IERC20(ticker);
IERC20(ticker).approve(sender, amount);
// Transfer Token To User
token.transferFrom(sender, recipient, amount);
emit Transfer(sender, recipient, amount);
}
}
it shows me this error:
Warning: Could not decode event!
execution failed due to an exception. Reverted
How can I solve this problem?
function deposit(address ticker,address sender,address recipient,uint256 amount
)
external payable
this function takes 4 args, you passed all the parameters but since it is payable, you have to also make sure from which account you are calling, so you need to add 5th parameter to the function:
await dex.deposit("0xa36085f69e2889c224210f603d836748e7dc0088", "0x5226a51522C23CcBEFd04a2d4C6c8e281eD1d680", "0xB643992c9fBcb1Cb06b6C9eb278b2ac35e6a2711", "1",
// you were missing this
{from:accounts[0])
In truffle test suite, when you create the contract, it passes the accounts as the first argument to the callback. You have to define accounts in before statement so when you run the tests those will be available on top level.
contract("Dex", (accounts) => {
let contractOwner = null;
let buyer = null;
let _contract = null;
before(async () => {
// Instead of Dex.new() try Dex.deployed()
// I am not sure if "new()" is still supported
_contract = await Dex.deployed();
contractOwner = accounts[0];
// sometimes you need to test a user except contract owner
buyer = accounts[1];
});
}
You need to approve
Inside ERC20.sol you are calling this:
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
uint256 currentAllowance = _allowances[sender][_msgSender()];
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
}
}
_transfer(sender, recipient, amount);
return true;
}
So you want DEX to transfer coin from another contract. So this another contract has to approve this transaction first. So I need to have another contract's address to be able to call approve
Related
All the contracts are compiling but error is depicted only in truffle test.
decentalBank.tests.js
// const { assert } = require('console')
const RWD = artifacts.require('RWD')
const Tether = artifacts.require('Tether')
const DecentralBank = artifacts.require('DecentralBank')
require('chai')
.use(require('chai-as-promised'))
.should()
contract('DecentralBank', ([owner, customer]) =>
{
let tether, rwd, decentralBank
function tokens(number)
{
return web3.utils.toWei(number, 'ether')
}
before(async () =>
{
//load contracts
tether = await Tether.new()
rwd = await RWD.new()
decentralBank = await DecentralBank.new(rwd.address, tether.address)
//transfer all tokens to DecentralBank (1 million)
await rwd.transfer(decentralBank.address, tokens('1000000'))
//transfer 100 mock tethers to customer
await tether.transfer(customer, tokens('100'), {from: owner})
})
describe('Mock Tether Deployment' , async () =>
{
it('matches name successfully', async () =>
{
let tether = await Tether.new()
const name = await tether.name()
assert.equal(name, 'Tether')
})
})
describe('Reward Token', async () =>
{
it ('matches name successfully' , async () =>
{
let reward = await RWD.new()
const name = await reward.name()
assert.equal(name, 'Reward Token')
})
})
describe('Decentral Bank Deployment', async () =>
{
it ('matches name successfully' , async () =>
{
let reward = await RWD.new()
const name = await reward.name()
assert.equal(name, 'Reward Token')
})
it('contract has tokens', async () =>
{
let balance = await rwd.balanceof(decentralBank.address)
assert.equal(balance, tokens('1000000'))
})
describe('Yeild Farming', async () =>
{
it('rewards tokens for staking', async () =>
{
let result
//check investor balance
result = await tether.balanceof(customer)
assert.equal(result.toString(), tokens('100'), 'customer mock wallet balance');
//check staking for customer of 100 tokens
await tether.approve(decentralBank.address, tokens('100'), {from: customer})
await decentralBank.depositTokens(tokens('100'), {from: customer})
//check updated balance of customer
result = await tether.balanceof(customer)
assert.equal(result.toString(), tokens('100'), 'customer mock wallet after staking 100 tokens')
//check updated balance of decentral bank
result = await tether.balanceof(decentralBank.address)
assert.equal(result.toString(), tokens('0'), 'decentral bank mock wallet balance after staking')
//(ACTUAL CODE)
// check updated balance of customer
// result = await tether.balanceof(customer)
// assert.equal(result.toString(), tokens('0'), 'customer mock wallet after staking 100 tokens')
// //check updated balance of decentral bank
// result = await tether.balanceof(decentralBank.address)
// assert.equal(result.toString(), tokens('100'), 'decentral bank mock wallet balance after staking')
//is staking balance
result = await decentralBank.isStaking(customer)
assert.equal(result.toString(), 'true', 'customer is staking status after staking')
//issue tokens
await decentralBank.issueTokens({from: owner})
//ensure only the owner can issue tokens
await decentralBank.issueTokens({from: customer}).should.be.rejected;
// //unstake tokens
await decentralBank.unstakeTokens({from: customer})
//check unstaking balances
result = await tether.balanceof(customer)
assert.equal(result.toString(), tokens('0'), 'customer mock wallet after unstaking ')
//check updated balance of decentral bank
result = await tether.balanceof(decentralBank.address)
assert.equal(result.toString(), tokens('100'), 'decentral bank mock wallet balance after staking')
//is staking balance
result = await decentralBank.isStaking(customer)
assert.equal(result.toString(), 'false', 'customer is no longer staking after unstaking')
})
})
})
})
DecentralBank.sol
pragma solidity ^0.5.0;
import './RWD.sol';
import './Tether.sol';
contract DecentralBank
{
string public name = 'Decentral Bank';
address public owner;
Tether public tether;
RWD public rwd;
address[] public stakers;
mapping(address => uint) public stakingBalance;
mapping(address => bool) public hasStaked;
mapping(address => bool) public isStaking;
constructor(RWD _rwd, Tether _tether) public
{
rwd = _rwd;
tether = _tether;
owner = msg.sender;
}
//staking function
function depositTokens(uint _amount) public
{
//require staking amount to be greater than zero
require(_amount > 0 ,'amount cannot be 0');
//trasfer tether tokens to this contract address for staking
tether.transferFrom(msg.sender, address(this), _amount);
//update staking balance
stakingBalance[msg.sender] = stakingBalance[msg.sender] + _amount;
if(!hasStaked[msg.sender])
{
stakers.push(msg.sender);
}
//update staking balance
isStaking[msg.sender] = true;
hasStaked[msg.sender] = true;
}
//unstake tokens
function unstakeTokens() public
{
uint balance = stakingBalance[msg.sender];
//require the amount to be greater then zero
require(balance > 0, 'Staking balance cant be less than zero');
//transfer the tokens to the specified contract address from our bank
tether.transfer(msg.sender, balance);
//reset staking balance
stakingBalance[msg.sender] = 0;
//update staking status
isStaking[msg.sender] = false;
}
//issue rewards
function issueTokens() public
{
//only owner can call this function
require(msg.sender == owner, 'caller must be the owner');
for(uint i=0; i<stakers.length; i++)
{
address recipient = stakers[i];
uint balance = stakingBalance[recipient] / 9; //9 to create percentage incentive
if(balance > 0)
{
rwd.transfer(recipient, balance);
}
}
}
}
Migrations.sol
pragma solidity ^0.5.0;
contract Migrations {
address public owner;
uint public last_completed_migration;
constructor() public {
owner = msg.sender;
}
modifier restricted(){
if (msg.sender == owner) _;
}
function setCompleted(uint completed) public restricted{
last_completed_migration = completed;
}
function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}
RWD.sol
pragma solidity ^0.5.0;
contract RWD {
string public name = 'Reward Token';
string public symbol = 'RWD';
uint256 public totalSupply = 1000000000000000000000000;
uint decimals = 18;
event Transfer(
address indexed _from,
address indexed _to,
uint _value
);
event Approval(
address indexed _owner,
address indexed _spender,
uint _value
);
mapping(address => uint256) public balanceof;
mapping(address=>mapping(address => uint256)) public allowance;
constructor() public
{
balanceof[msg.sender] = totalSupply;
}
function transfer(address _to, uint _value)public returns (bool success)
{
//require that the value is greater or equal for transfer
require(balanceof[msg.sender]>=_value);
//transfer the amount and subtract the balance
balanceof[msg.sender] -= _value;
//add the balance
balanceof[_to] += _value;
emit Transfer(msg.sender ,_to, _value);
return true;
}
function approve(address _spender,uint256 _value) public returns (bool success)
{
allowance[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value )public returns (bool success)
{
require(_value <=balanceof[_from]);
require(_value <=allowance[_from][msg.sender]);
//add the balance for transactionFrom
balanceof[_to]+= _value;
//subtract the balance for transactionFrom
balanceof[_to]-= _value;
allowance[msg.sender][_from]-= _value;
emit Transfer(_from ,_to, _value);
return true;
}
}
Tether.sol
pragma solidity ^0.5.0;
contract Tether {
string public name = 'Tether';
string public symbol = 'USDT';
uint256 public totalSupply = 1000000000000000000000000;
uint decimals = 18;
event Transfer(
address indexed _from,
address indexed _to,
uint _value
);
event Approval(
address indexed _owner,
address indexed _spender,
uint _value
);
mapping(address => uint256) public balanceof;
mapping(address=>mapping(address => uint256)) public allowance;
constructor() public
{
balanceof[msg.sender] = totalSupply;
}
function transfer(address _to, uint _value)public returns (bool success)
{
//require that the value is greater or equal for transfer
require(balanceof[msg.sender] >= _value);
//transfer the amount and subtract the balance
balanceof[msg.sender] -= _value;
//add the balance
balanceof[_to] += _value;
emit Transfer(msg.sender ,_to, _value);
return true;
}
function approve(address _spender, uint256 _value) public returns (bool success)
{
allowance[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value )public returns (bool success)
{
require(_value <=balanceof[_from]);
require(_value <=allowance[_from][msg.sender]);
//add the balance for transactionFrom
balanceof[_to] += _value;
//subtract the balance for transactionFrom
balanceof[_to] -= _value;
allowance[msg.sender][_from] -= _value;
emit Transfer(_from, _to, _value);
return true;
}
}
ERROR: AFTER TRUFFLE TEST COMMAND
Contract: DecentralBank
Decentral Bank Deployment
Yeild Farming
rewards tokens for staking:
Error: Returned error: VM Exception while processing transaction: revert
at Context. (test\decentalBank.tests.js:98:69)
at runMicrotasks ()
at processTicksAndRejections (node:internal/process/task_queues:96:5)
I have minted some NFTs on opensea. These are on Polygon Mumbai network. Now I want to transfer these to token to other addresses using alchemy web3. Here is the code I am using.
Note: This is supposed to run in nodejs restful API, so there is no wallet available that why I am manually signing the transaction.
async function main() {
require('dotenv').config();
const { API_URL,API_URL_TEST, PRIVATE_KEY } = process.env;
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL_TEST);
const myAddress = '*************************'
const nonce = await web3.eth.getTransactionCount(myAddress, 'latest');
const transaction = { //I believe transaction object is not correct, and I dont know what to put here
'asset': {
'tokenId': '******************************',//NFT token id in opensea
},
'gas': 53000,
'to': '***********************', //metamask address of the user which I want to send the NFT
'quantity': 1,
'nonce': nonce,
}
const signedTx = await web3.eth.accounts.signTransaction(transaction, PRIVATE_KEY);
web3.eth.sendSignedTransaction(signedTx.rawTransaction, function(error, hash) {
if (!error) {
console.log("🎉 The hash of your transaction is: ", hash, "\n Check Alchemy's Mempool to view the status of your transaction!");
} else {
console.log("âť—Something went wrong while submitting your transaction:", error)
}
});
}
main();
Assumed that you have Metamask installed in your browser, and that the NFT smart contract follows ERC721 Standard
const { API_URL,API_URL_TEST, PRIVATE_KEY } = process.env;
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const {abi} = YOUR_CONTRACT_ABI
const contract_address = CONTRACT ADDRESS
require('dotenv').config();
async function main() {
const web3 = createAlchemyWeb3(API_URL_TEST);
web3.eth.getAccounts().then(accounts => {
const account = account[0]
const nameContract = web3.eth.Contract(abi, contract_address);
nameContract.methods.transfer(account, ADDRESS_OF_WALLET_YOU_WANT_TO_SEND_TO, TOKEN_ID).send();
})
.catch(e => console.log(e));
}
main();
Had the same problem, because there is no example on transferring NFT token.
There is a well explained 3-parts-example on ethereum website to mint an NFT.
In the first part, step 10, it explains how to write a contract and also mentions the existing methods in the contract object extended:
After our import statements, we have our custom NFT smart contract, which is surprisingly short — it only contains a counter, a constructor, and single function! This is thanks to our inherited OpenZeppelin contracts, which implement most of the methods we need to create an NFT, such as ownerOf which returns the owner of the NFT, and transferFrom, which transfers ownership of the NFT from one account to another.
So, with these informations, I made an NFT transfer transaction between two addresses with my metamask mobile app. Then I searched the JSON of this transaction through etherscan API.
In this way, I was able to transfer tokens to other addresses using alchemy web3 with this script:
require("dotenv").config()
const API_URL = process.env.API_URL; //the alchemy app url
const PUBLIC_KEY = process.env.PUBLIC_KEY; //my metamask public key
const PRIVATE_KEY = process.env.PRIVATE_KEY;//my metamask private key
const {createAlchemyWeb3} = require("#alch/alchemy-web3")
const web3 = createAlchemyWeb3(API_URL)
const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json")//this is the contract created from ethereum example site
const contractAddress = "" // put here the contract address
const nftContract = new web3.eth.Contract(contract.abi, contractAddress)
/**
*
* #param tokenID the token id we want to exchange
* #param to the metamask address will own the NFT
* #returns {Promise<void>}
*/
async function exchange(tokenID, to) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest');
//the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': 500000,
'input': nftContract.methods.safeTransferFrom(PUBLIC_KEY, to, tokenID).encodeABI() //I could use also transferFrom
};
const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY)
signPromise
.then((signedTx) => {
web3.eth.sendSignedTransaction(
signedTx.rawTransaction,
function (err, hash) {
if (!err) {
console.log(
"The hash of your transaction is: ",
hash,
"\nCheck Alchemy's Mempool to view the status of your transaction!"
)
} else {
console.log(
"Something went wrong when submitting your transaction:",
err
)
}
}
)
})
.catch((err) => {
console.log(" Promise failed:", err)
})
}
I Had the same problem. I need to transfer NFT in node.js back-end.
I use network provider to use Moralis NetworkWeb3Connector.
here's my repository for example:
https://github.com/HanJaeJoon/Web3API/blob/2e30e89e38b7b1f947f4977a0fe613c882099fbc/views/index.ejs#L259-L275
await Moralis.start({
serverUrl,
appId,
masterKey,
});
await Moralis.enableWeb3({
// rinkeby
chainId: 0x4,
privateKey: process.env.PRIVATE_KEY,
provider: 'network',
speedyNodeApiKey: process.env.MORALIS_SPEEDY_NODE_API_KEY,
});
const options = {
type,
receiver,
contractAddress,
tokenId,
amount: 1,
};
try {
await Moralis.transfer(options);
} catch (error) {
console.log(error);
}
you can get speed node api key in
Moralis dashboad > Networks > Eth Rinkeby(in my case) > Settings
screenshot
I am new to the firebase (and all its features) space. I have read the documentation, and I have been able to use the web sdk properly. I have created a file where all my current firebase code is written as seen in firebaseApi.js below. Also, below is an example of how I have used the functions under registration.js (Kindly correct if I am doing it wrong), the sample works. I was trying to implement
admin.auth().getUserByPhoneNumber(phoneNumber),
which I want to use to check if a currently inputted phone number already exists in the App. But I have read the Admin SDKs cannot be used in client-side environments and should only be used in privileged server environments owned or managed by the developers of a Firebase app. I am kinda lost on how to go around this.
is it possible to connect firebase cloud functions to the client-side like
I am doing with the firebaseApi?
I have cleaned up the code and kept only the relevant parts
firebaseApi.js
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';
import 'firebase/database';
import 'firebase/storage';
const config = {config};
firebase.initializeApp(config);
class Firebase {
register = ({ fullname, email, phone }) => {
const user = Firebase.auth.currentUser.uid;
const firestoreRef = Firebase.firestore.collection('Users').doc(user);
const settings = {
fullname,
email,
phone,
};
firestoreRef
.set(settings);
};
static init() {
Firebase.auth = firebase.auth();
Firebase.firestore = firebase.firestore();
Firebase.database = firebase.database();
Firebase.storage = firebase.storage();
Firebase.email = firebase.auth.EmailAuthProvider;
Firebase.google = firebase.auth.GoogleAuthProvider;
Firebase.phoneVerify = new firebase.auth.PhoneAuthProvider();
Firebase.phone = firebase.auth.PhoneAuthProvider;
}
}
Firebase.shared = new Firebase();
export default Firebase;
registration.js
import Firebase from './firebaseApi';
onCompleteReg() {
const { fullname, email, email } = this.state;
const settings = {
fullname,
email,
email
};
Firebase.shared
.registerSettings(settings)
.then(() => {
console.log('Successful');
}).catch((e) => {
console.log(e);
})
}
As a matter of privacy and best practices, unless the current user is an administrator, I would not be exposing the ability to check if any given phone number is used by any individual and/or is tied to your application.
Wrapped in Cloud Function
As the Admin SDK is to be used only from secure environments, you can only expose it's functionality by way of some API. It is beneficial in this case to handle user authentication and CORS automatically, so I'll use a Callable Function. Based on the sensitive nature of such an API, it would also be advised to rate-limit access to it which can be easily achieved using the firebase-functions-rate-limiter package. In the below code, we limit the API calls to 2 uses per user and 10 uses across all users, per 15 second period to prevent abuse.
import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';
import { FirebaseFunctionsRateLimiter } from 'firebase-functions-rate-limiter';
admin.initializeApp();
const realtimeDb = admin.database();
const perUserLimiter = FirebaseFunctionsRateLimiter.withRealtimeDbBackend(
{
name: 'rate-limit-phone-check',
maxCalls: 2,
periodSeconds: 15,
},
realtimeDb
);
const globalLimiter = FirebaseFunctionsRateLimiter.withRealtimeDbBackend(
{
name: 'rate-limit-phone-check',
maxCalls: 10,
periodSeconds: 15,
},
realtimeDb
);
exports.phoneNumber = functions.https.onCall(async (data, context) => {
// assert required params
if (!data.phoneNumber) {
throw new functions.https.HttpsError(
'invalid-argument',
'Value for "phoneNumber" is required.'
);
} else if (!context.auth || !context.auth.uid) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called while authenticated.'
);
}
// rate limiter
const [userLimitExceeded, globalLimitExceeded] = await Promise.all(
perUserLimiter.isQuotaExceededOrRecordUsage('u_' + context.auth.uid),
globalLimiter.isQuotaExceededOrRecordUsage('global'));
if (userLimitExceeded || globalLimitExceeded) {
throw new functions.https.HttpsError(
'resource-exhausted',
'Call quota exceeded. Try again later',
);
}
let userRecord = await admin.auth.getUserByPhoneNumber(phoneNumber);
return userRecord.uid;
}
To call the check, you would use the following code on the client:
let checkPhoneNumber = firebase.functions().httpsCallable('phoneNumber');
checkPhoneNumber({phoneNumber: "61123456789"})
.then(function (result) {
let userId = result.data;
// do something with userId
})
.catch(function (error) {
console.error('Failed to check phone number: ', error)
});
Attempt by Login
Rather than allow users to find out if a phone number exists or specifically exists on your service, it is best to follow the Phone Number authentication flow and allow them to prove that they own a given phone number. As the user can't verify more than one number en-masse, this is the safest approach.
From the Firebase Phone Auth Reference, the following code is used to verify a phone number:
// 'recaptcha-container' is the ID of an element in the DOM.
var applicationVerifier = new firebase.auth.RecaptchaVerifier(
'recaptcha-container');
var provider = new firebase.auth.PhoneAuthProvider();
provider.verifyPhoneNumber('+16505550101', applicationVerifier)
.then(function(verificationId) {
var verificationCode = window.prompt('Please enter the verification ' +
'code that was sent to your mobile device.');
return firebase.auth.PhoneAuthProvider.credential(verificationId,
verificationCode);
})
.then(function(phoneCredential) {
return firebase.auth().signInWithCredential(phoneCredential);
});
Privileged Phone Search
If you want an appropriately privileged user (whether they have an administrator or management role) to be able to query users by a phone number, you can use the following scaffolding. In these code samples, I limit access to those who have the isAdmin claim on their authentication token.
Database structure: (see this answer for more info)
"phoneNumbers": {
"c011234567890": { // with CC for US
"userId1": true
},
"c611234567890": { // with CC for AU
"userId3": true
},
...
}
Database rules:
{
"rules": {
...,
"phoneNumbers": {
"$phoneNumber": {
"$userId": {
".write": "auth.uid === $userId && (!newData.exists() || root.child('users').child(auth.uid).child('phoneNumber').val() == ($phoneNumber).replace('c', ''))" // only this user can edit their own record and only if it is their phone number or they are deleting this record
}
},
".read": "auth != null && auth.token.isAdmin == true", // admins may read/write everything under /phoneNumbers
".write": "auth != null && auth.token.isAdmin == true"
}
}
}
Helper functions:
function doesPhoneNumberExist(phoneNumber) {
return firebase.database.ref("phoneNumbers").child("c" + phoneNumber).once('value')
.then((snapshot) => snapshot.exists());
}
// usage: let exists = await doesPhoneNumberExist("611234567890")
function getUsersByPhoneNumber(phoneNumber) {
return firebase.database.ref("phoneNumbers").child("c" + phoneNumber).once('value')
.then((snapshot) => snapshot.exists() ? Object.keys(snapshot.val()) : []);
}
// usage: let usersArray = await getUsersByPhoneNumber("611234567890") - normally only one user
function searchPhoneNumbersThatStartWith(str) {
if (!str || str.length < 5) return Promise.reject(new Error('Search string is too short'));
return firebase.database.ref("phoneNumbers").startAt("c" + str).endAt("c" + str + "\uf8ff").once('value')
.then((snapshot) => {
let phoneNumbers = [];
snapshot.forEach((phoneEntrySnapshot) => phoneNumbers.push(phoneEntrySnapshot.key));
return phoneNumbers;
});
}
// usage: let matches = await searchPhoneNumbersThatStartWith("61455")
// best handled by Cloud Function not client
function linkPhoneNumberWithUser(phoneNumber, userId) {
return firebase.database.ref("phoneNumbers").child("c" + phoneNumber).child(userId).set(true);
}
// usage: linkPhoneNumberWithUser("611234567890", firebase.auth().currentUser.uid)
// best handled by Cloud Function not client
function unlinkPhoneNumberWithUser(phoneNumber, userId) {
return firebase.database.ref("phoneNumbers").child("c" + phoneNumber).child(userId).remove();
}
// usage: unlinkPhoneNumberWithUser("611234567890", firebase.auth().currentUser.uid)
Context
I have a quorum network mounted following the example of 7nodes. In node 1 I have deployed a smart contract privately, putting the public key of this "BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo=" in the private form.
My goal is to make a transfer to this smart contract, to execute one of its functions. Being a private contract the transfer has to be signed.
All this from a nodejs server.
Code
The smart contract is the following:
pragma solidity ^0.4.19;
contract SimpleStorage {
uint storedData;
uint secondData;
uint TData;
function set(uint x) external {
storedData = x;
}
function setSecond(uint x) external {
secondData = x;
}
function setT(uint x) external {
TData = x;
}
function get() external view returns (uint retVal) {
return storedData;
}
function getSecond() external view returns (uint retVal) {
return secondData;
}
function getT() external view returns (uint retVal) {
return TData;
}
}
The code in nodejs is the following:
const Web3 = require('web3');
const EthereumTx = require('ethereumjs-tx');
const contract = require('truffle-contract');
const simpleStorageInterface = require("./contracts/SimpleStorage.json");
var web3 = new Web3(
new Web3.providers.HttpProvider('http://172.10.4.159:22000')
);
var generalInfo = {
contractAddress: "0x1932c48b2bf8102ba33b4a6b545c32236e342f34",
account: "0xed9d02e382b34818e88b88a309c7fe71e65f419d",
keystore: {"address":"ed9d02e382b34818e88b88a309c7fe71e65f419d","crypto":{"cipher":"aes-128-ctr","ciphertext":"4e77046ba3f699e744acb4a89c36a3ea1158a1bd90a076d36675f4c883864377","cipherparams":{"iv":"a8932af2a3c0225ee8e872bc0e462c11"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"8ca49552b3e92f79c51f2cd3d38dfc723412c212e702bd337a3724e8937aff0f"},"mac":"6d1354fef5aa0418389b1a5d1f5ee0050d7273292a1171c51fd02f9ecff55264"},"id":"a65d1ac3-db7e-445d-a1cc-b6c5eeaa05e0","version":3},
bytecode: "0x608060405234801561001057600080fd5b506101ec806100206000396000f300608060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631b03316f1461007d57806322554f34146100a857806360fe47b1146100d35780636d4ce63c14610100578063b698c1291461012b578063f5f3194114610158575b600080fd5b34801561008957600080fd5b50610092610185565b6040518082815260200191505060405180910390f35b3480156100b457600080fd5b506100bd61018f565b6040518082815260200191505060405180910390f35b3480156100df57600080fd5b506100fe60048036038101908080359060200190929190505050610199565b005b34801561010c57600080fd5b506101156101a3565b6040518082815260200191505060405180910390f35b34801561013757600080fd5b50610156600480360381019080803590602001909291905050506101ac565b005b34801561016457600080fd5b50610183600480360381019080803590602001909291905050506101b6565b005b6000600154905090565b6000600254905090565b8060008190555050565b60008054905090565b8060018190555050565b80600281905550505600a165627a7a723058209cc3ec1ccb2383d74522ba2e5974347862883f4c4aa826bb69e6e1cfcc5bdd110029",
interface: [{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"setSecond","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"setT","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getSecond","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getT","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}],
privateFor: "BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo="
}
web3.eth.getAccounts().then(async (_result) => {
// Set web3 default account
web3.eth.defaultAccount = _result[0];
// Get truffle-contract
let myContract = contract(simpleStorageInterface);
// Set provider
myContract.setProvider(web3.currentProvider);
if (typeof myContract.currentProvider.sendAsync !== "function") {
myContract.currentProvider.sendAsync = function() {
return myContract.currentProvider.send.apply(
myContract.currentProvider, arguments
);
};
}
// Instanciate the contract
var contractInstance = new web3.eth.Contract(simpleStorageInterface.abi, generalInfo.contractAddress);
// Function data
let encodedABI = contractInstance.methods.set(123).encodeABI();
let nonce = await web3.eth.getTransactionCount(web3.eth.defaultAccount);
// Transaction
let tx = {
nonce: web3.utils.toHex(nonce),
from: generalInfo.account,
to: generalInfo.contractAddress,
gas: 2000000,
gasPrice: 0,
data: encodedABI,
privateFor: [generalInfo.privateFor]
}
let decrypt = web3.eth.accounts.decrypt(generalInfo.keystore, "");
// Remove 0x from private key
var privateKey = decryptKey.privateKey.substring(2);
var bufferPK = new Buffer(privateK, 'hex');
// Generate transaction using "ethereumjs-tx"
var transaction = new EthereumTx(tx);
// Sign transaction
transaction.sign(bufferPK);
// Send signed transaction
web3.eth.sendSignedTransaction("0x" + transaction.serialize().toString('hex'), (_err, _res) => {
if(_err){
console.error("ERROR: ", _err);
} else {
console.log("Success: ", _res);
}
}).on('confirmation', (confirmationNumber, receipt) => {
console.log('=> confirmation: ' + confirmationNumber);
})
.on('transactionHash', hash => {
console.log('=> hash');
console.log(hash);
})
.on('receipt', receipt => {
console.log('=> reciept');
console.log(receipt);
})
.on('error', console.error);
});
Question and problem
The transaction is executed successfully, but the new value "123" is not changed in the smart contract. Only if I unlock the account before the transaction await web3.eth.personal.unlockAccount (account, "") works really, but I do not want to be blocking and unblocking the account for security reasons.
A correct transfer would be the following:
{
blockHash: "0xe5a2df3f592392c71f9995d697721c046f60c81d2988418b0c7b929cb17a0cee",
blockNumber: 2190,
from: "0xed9d02e382b34818e88b88a309c7fe71e65f419d",
gas: 90000,
gasPrice: 0,
hash: "0x24df9e01d9fdb7acc7a2842dcdd1d93a37e7be7885ef469a262d8a690f7143f3",
input: "0xb0101ef545cf42bb490bca4fac873ea06d989abf1fbdc89f7dfd09014c085f163c371efa5acac2b43c9dec5cb20bd11e853069a99f4bcb938d6fdcd6f2918333",
nonce: 31,
r: "0x9851006a766b4bd75051cdba9c06b6d251125d68894983eee3a4c1a53a03d77a",
s: "0x1696039cedf14a82147c858bc17896664c9c74bda313307dbf9386b7d6893938",
to: "0x1932c48b2bf8102ba33b4a6b545c32236e342f34",
transactionIndex: 0,
v: "0x25",
value: 0
}
Mine looks like this:
{
blockHash: "0x6a2aacceabe984b2c368fa9ca7c245065924dd6d88e30f81311e2a5a7e2aeab8",
blockNumber: 2119,
from: "0xed9d02e382b34818e88b88a309c7fe71e65f419d",
gas: 2000000,
gasPrice: 0,
hash: "0x4a918c1641478c1f229e7cdfff93669a6e08b37555eafe871fc3b05717cbcb79",
input: "0x60fe47b1000000000000000000000000000000000000000000000000000000000000007b",
nonce: 30,
r: "0xac64a34de4bf0c2d3f1d8da6af3d38ee12be38846f744004eeef460ad94b528e",
s: "0x10e642925665877c4e2571a2f835af68c025417574462ffc4864e6128e4a4deb",
to: "0x1932c48b2bf8102ba33b4a6b545c32236e342f34",
transactionIndex: 0,
v: "0x1c",
value: 0
}
The difference is the property "v", the value that is being fixed in my transfer is not correct, it has to be 0x25 or 0x26 to identify them as private transfers. I do not understand why these values are not setting well, helpme please.
Is there any way in which I can get Historian for a particular participant in hyperledger-composer using node API?
I am developing an application based on hyperledger-composer using Node APIs.I want to show the history of transaction of a particular participant in his/her profile. I have created the permission.acl for that and that is working fine in playground. But when i am accessing the historian from node API it is giving complete historian of the network. I don't know how to filter that for a participant.
you can return results from REST API calls since v0.20 to the calling client application, so something like the following would work (not tested, but you get the idea). NOTE: You could just call the REST API end (/GET Trader) direct via REST with your parameter (or whatever endpoints you create for your own business network - the example below is trade-network), rather than the example of using 'READ-ONLY' Transaction processor Endpoint described below, for returning larger result sets to your client application. See more on this in the docs
NODE JS Client using APIs:
const BusinessNetworkConnection = require('composer-client').BusinessNetworkConnection;
const rp = require('request-promise');
this.bizNetworkConnection = new BusinessNetworkConnection();
this.cardName ='admin#mynet';
this.businessNetworkIdentifier = 'mynet';
this.bizNetworkConnection.connect(this.cardName)
.then((result) => {
//You can do ANYTHING HERE eg.
})
.catch((error) => {
throw error;
});
// set up my read only transaction object - find the history of a particular Participant - note it could equally be an Asset instead !
var obj = {
"$class": "org.example.trading.MyPartHistory",
"tradeId": "P1"
};
async function callPartHistory() {
var options = {
method: 'POST',
uri: 'http://localhost:3000/api/MyPartHistory',
body: obj,
json: true
};
let results = await rp(options);
// console.log("Return value from REST API is " + results);
console.log(" ");
console.log(`PARTICIPANT HISTORY for Asset ID: ${results[0].tradeId} is: `);
console.log("=============================================");
for (const part of results) {
console.log(`${part.tradeId} ${part.name}` );
}
}
// Main
callPartHistory();
//
MODEL FILE
#commit(false)
#returns(Trader[])
transaction MyPartHistory {
o String tradeId
}
READ-ONLY TRANSACTION PROCESSOR CODE (in 'logic.js') :
/**
* Sample read-only transaction
* #param {org.example.trading.MyPartHistory} tx
* #returns {org.example.trading.Trader[]} All trxns
* #transaction
*/
async function participantHistory(tx) {
const partId = tx.tradeid;
const nativeSupport = tx.nativeSupport;
// const partRegistry = await getParticipantRegistry('org.example.trading.Trader')
const nativeKey = getNativeAPI().createCompositeKey('Asset:org.example.trading.Trader', [partId]);
const iterator = await getNativeAPI().getHistoryForKey(nativeKey);
let results = [];
let res = {done : false};
while (!res.done) {
res = await iterator.next();
if (res && res.value && res.value.value) {
let val = res.value.value.toString('utf8');
if (val.length > 0) {
console.log("#debug val is " + val );
results.push(JSON.parse(val));
}
}
if (res && res.done) {
try {
iterator.close();
}
catch (err) {
}
}
}
var newArray = [];
for (const item of results) {
newArray.push(getSerializer().fromJSON(item));
}
console.log("#debug the results to be returned are as follows: ");
return newArray; // returns something to my NodeJS client (called via REST API)
}