I'm studying the Solidity, and I want to understand how to interact with a smartcotnract and etherjs.
I have a simple function like this:
function buyNumber(uint256 _number) public payable {
if(msg.value < 0.1 ether){
revert("more eth!");
}
...todoStuff
}
And I have the test
const tx = {
from: owner.address,
to: myContract.address,
value: ethers.utils.parseEther('0.1'),
data: '0x4b729aff0000000000000000000000000000000000000000000000000000000000000001'
}
let sendTx = await owner.sendTransaction(tx);
console.log(sendTx)
Now, the transaction works because I get 0x4b729aff0000000000000000000000000000000000000000000000000000000000000001 the function signature and parameters with
let iface = new ethers.utils.Interface(ABI)
iface.encodeFunctionData("buyNumber", [ 1 ])
0x4b729aff0000000000000000000000000000000000000000000000000000000000000001
Can I get the same result in easy way? How can I call a function in my contract with params? I could put the msg.value as parameter, but I prefer to use less parameters
You can use the ethers Contract helper class, which allows invoking its methods in a more developer-friedly way, based on the provided contract ABI JSON.
It does the same thing in the background as your script - encodes the function call to the data value.
const contract = new ethers.Contract(contractAddress, abiJson, signerInstance);
// the Solidity function accepts 1 param - a number
// the last param is the `overrides` object - see docs below
await contract.buyNumber(1, {
value: ethers.utils.parseEther('0.1')
});
You can also use the overrides object to specify non-default transaction params. Such as its value (the default value is 0).
Related
I've built a simple smart contract to run on the Ethereum blockchain and I'm trying to replicate some of it's behavior on Solana. After making some slight changes I've managed to compile the program with Solang targeting Solana, but I'm not sure how to go about calling the methods; there doesn't seem to be a great wealth of documentation or examples on this. For example, if my program were written as follows:
contract Example {
function foo(...) { ... }
function bar(...) { ... }
}
How would I specify a call to foo vs a call to bar? Furthermore, how would I encode the arguments for these method calls?
Currently my approach is to use the #solana/buffer-layout library to encode my arguments as a struct, starting with lo.u8('instruction') to specify the method call (in the example case, I assume 0 would refer to foo and 1 would refer to bar). I've taken this approach based on looking at the source code for #solana/spl-token (specifically this file and it's dependencies) but I'm not sure if it will work for a program compiled using Solang, and the buffer layout encoding has been throwing an unexpected error as well:
TypeError: Blob.encode requires (length 32) Uint8Array as src
The code throwing this error is as follows:
const method = lo.struct([
lo.u8('instruction'),
lo.seq(
lo.blob(32),
lo.greedy(lo.blob(32).span),
'publicKeys',
),
])
const data = Buffer.alloc(64); // Using method.span here results in an error, as method.span == -1
method.encode(
{
instruction: 0,
publicKeys: [firstPublicKey, secondPublicKey],
},
data,
);
While this type error seems obvious, it doesn't line up with the sample code in the solana-labs/solana-program-library repository. I'm pretty sure this problem has to do with my use of lo.seq() but I'm not sure what the problem is.
Is my approach to this correct besides this type error, or is my approach fundamentally wrong? How can I call the intended method with encoded arguments? Thank you for any help.
There's a better library for you to use, #solana/solidity, which has a Contract class to encapsulate calls on the contract.
For example, in your case, you could do:
const { Connection, LAMPORTS_PER_SOL, Keypair } = require('#solana/web3.js');
const { Contract, Program } = require('#solana/solidity');
const { readFileSync } = require('fs');
const EXAMPLE_ABI = JSON.parse(readFileSync('./example.abi', 'utf8'));
const PROGRAM_SO = readFileSync('./example.so');
(async function () {
console.log('Connecting to your local Solana node ...');
const connection = new Connection('http://localhost:8899', 'confirmed');
const payer = Keypair.generate();
console.log('Airdropping SOL to a new wallet ...');
const signature = await connection.requestAirdrop(payer.publicKey, LAMPORTS_PER_SOL);
await connection.confirmTransaction(signature, 'confirmed');
const program = Keypair.generate();
const storage = Keypair.generate();
const contract = new Contract(connection, program.publicKey, storage.publicKey, EXAMPLE_ABI, payer);
await contract.load(program, PROGRAM_SO);
console.log('Program deployment finished, deploying the example contract ...');
await contract.deploy('example', [true], program, storage);
const res = await contract.functions.foo();
console.log('foo: ' + res.result);
const res2 = await contract.functions.bar();
console.log('bar: ' + res2.result);
})();
Example adapted from https://github.com/hyperledger-labs/solang#build-for-solana
More information about the package at https://www.npmjs.com/package/#solana/solidity
I have a component that displays balances for 4 separate tokens. I'd like to have a function like this:
async updateTokenBalance(token, balance) {
balance = await getTokenBalance(token);
balance = balance.value.uiAmount;
}
which I can call like so:
updateTokenBalance(this.a_acc, this.a_balance);
updateTokenBalance(this.b_acc, this.b_balance);
updateTokenBalance(this.c_acc, this.c_balance);
updateTokenBalance(this.d_acc, this.d_balance);
unfortunately this doesn't seem to work. The only way I've gotten it to work so far is by having 4 separate functions:
async updateATokenBalance() {
let balance = await getTokenBalance(this.a_acc);
this.a_balance = balance.value.uiAmount;
}
// the other 3 analogous
in particular, it's the second line of the function where I try to assign the balance to a data property of this.a_balance that breaks if this.a_balance is passed in as arg.
Is there a way to make it work?
If your data come from an API, you should iterate through the incoming object list and run the function for each item with the analogous arguments.
I want to determine a view function of a smart contract result in web3js 1.x
Suppose we have a view function getName(uint code) and want to get the name of a person having his code. So we write:
contract.methods.getName(code).call(option)
.then(...)
.catch(...);
We want to define a function to return the result name and do something with it.
How can we relate the defined function and method call?
For example:
async function name(code) {
contract.methods.getName(code).call(option)
.then(...)
.catch(...);
return ???
}
for (let i = 0; i <= 10; i++) {
let x = name(i);
// Do something with x, for example:
alert(x);
}
P.S: I know that the result is accessible inside .then body, but I want to access it inside the name function scope and return it from the function.
You already using async so you can just use await
var result = await contract.methods.getName(code).call(option)
I have this middleware on my app that checks the user role for a route:
public function handle($request, Closure $next, ...$roles)
{
if (in_array($request->user()->rol, $roles)) {
return $next($request);
} else {
return redirect()->action('SecurityController#noAutorizado');
}
}
And I'm triying to make a test for this middleware (phpUnit):
public function testUsuarioLogadoPuedeAccederAPantallaUsuarios()
{
$user = UsuariosTestFixtures::unAsignador();
$this->actingAs($user);
$request = Request::create('/usuarios', 'GET');
$middleware = new CheckRole();
$response = $middleware->handle($request,Closure $next,$user->getRole(), function () {});
$this->assertEquals($response, true);
}
But i'm retreiving this error: Argument 2 passed to App\Http\Middleware\CheckRole::handle() must be an instance of Closure, null given
I don't know how I have to pass the "Closure $next" on the $middleware->handle
I've tryed this:
public function testUsuarioLogadoPuedeAccederAPantallaUsuarios(Closure $next){...}
But It returns an error: Too few arguments to function UsuarioControllerTest::testUsuarioLogadoPuedeAccederAPantallaUsuarios(), 0 passed in C:\www\APPS\catsa\vendor\phpunit\phpunit\src\Framework\TestCase.php
What's the solution?
Thanks a lot!
A Closure in PHP is simply a function, so you need to pass a function as the second argument of your handle method.
In the context of Laravel middleware, the $next function represent the full pipeline of steps that the request goes through.
Obviously you can't (and don't need to) execute this pipeline during a test. What you need is just a function that return some values that your can test in an assertion.
What you can do is something like this:
//... setup code here
$middleware = new CheckRole();
$roles = ['role1', 'role2']; // change this with the desired roles
$result = $middleware->handle($request,function($request) {
return 'success';
},$roles);
$this->assertEquals('success', $result);
So, what is happening here?
If everything goes as planned (the user has the required role), the $next closure is executed and it returns success; on the other hand, if the user doesn't have the required role, the code takes the other path and it returns a RedirectResponse.
Finally, the assertion checks if success is returned, and it reports a failure if that doesn't happen.
I have the following script that is calling a text file:
/* first create a new instance of the LoadVars object */
myVariables = new LoadVars();
myVariables.load("myFile.txt");
myVariables.onLoad = function(getreading):String{
var ODOMETER2:String=myVariables.ACADEMICWATER;
return ODOMETER2;
trace (ODOMETER2);
}
trace(getreading());
The text file contains the following:
ACADEMICWATER=3002&elec=89
I am able to import the value of 3002 into the function and I can trace it. However, I Should be able to trace it outside the function using trace(getreading()); as shown on the last line. This only returns an "UNDEFINED" value. I am stumped.
You are declaring an anonymous function (see AS3 Syntax and language / Functions) which can't be referenced by name. getreading is declared in your code as an untyped parameter of this function.
If you want to trace the result of this function, then you should declare a named function like this:
function getReading(): String {
var ODOMETER2:String=myVariables.ACADEMICWATER;
return ODOMETER2;
}
myVariables.onLoad = getReading;
trace(getReading());
getreading is not the name of the function in this case, but the name of a parameter to the anonymous function that is run on the onLoad event of the myVariables object.
Place the variable ODOMETER2 outside the function and set it's value inside the anonymous function. Then you will be able to access it outside the function as well.
/* first create a new instance of the LoadVars object */
var ODOMETER2:String;
myVariables = new LoadVars();
myVariables.load("myFile.txt");
myVariables.onLoad = function(){
ODOMETER2=myVariables.ACADEMICWATER;
}
trace(ODOMETER2);
LoadVars.onLoad is an event handler. It is called by LoadVars as soon as it finishes with the asynchronous load operation. It takes a boolean argument, indicating success or failure of the operation. It does not return anything.
LoadVars.onLoad documentation
In that function, you typically act upon the data you received, like storing and processing it. Here's a very simple example showing some basic use cases:
var ODOMETER2:String;
var myVariables = new LoadVars();
myVariables.load("myFile.txt");
myVariables.onLoad = function(success) {
trace(success);
ODOMETER2 = myVariables.ACADEMICWATER;
processResults();
}
function processResults() {
trace(ODOMETER2);
trace(myVariables.ACADEMICWATER);
}
// traces:
// true
// 3002
// 3002