I know that the fallback is triggered when sending a transaction to this contract and calling a non-existent function, what I want to understand is: Contract A sends a transaction to Contract B (fallback), the solidity code has run to here The fallback of Contract B is Is it triggered at this time or the fallback is triggered after the transaction sent by A to B has completed the block confirmation!
Former is correct. Fallback function triggers while contract B has sent the transaction to contract A.
Why?
Because Let's say contract A records the number of invalid function calls for which the fallback is called.
// Contract A
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract FallbackContract {
uint256 public invalidFunctionCalls;
constructor () {
invalidFunctionCalls = 0;
}
fallback() external {
invalidFunctionCalls += 1;
}
}
Now, for each invalid function call, the variable invalidFunctionCalls should be incremented in the current block to reflect the invalid call thereafter. Which means fallback function is called as soon as the invalid function call is triggered.
Related
My bot makes swaps using UniswapV2Router from basetoken->token then token-> basetoken, from a random list using the following method.
const tx = await swapper.connect(owner).duelDexTrade(router1,router2,baseToken,token,amount)
await tx.wait()
console.log(tx)
I approve amounts for the router(spookyswap or spiritswap) the amouts before each swap.
function swap (address _router, address _tokenIn, address _tokenOut, uint _amount) private onlyOwner {
IERC20(_tokenIn).approve(_router,_amount); // Approve router to spend.
address[] memory path;
path = new address[](2);
path[0] = _tokenIn;
path[1] = _tokenOut;
uint deadline = block.timestamp+300;
IUniswapV2Router02(_router).swapExactTokensForTokens(_amount,1,path, address(this), deadline);
}
When I run the console, the first transaction (irrespective of the pair) always goes through, and the following transactions all fail with the error:
Error: cannot estimate gas; transaction may fail or may require manual gas limit
reason="execution reverted: TransferHelper: TRANSFER_FROM_FAILED"
When I stop the console and start again, the first transaction goes through followed by the above error. Every time same result. I am using hh and ethersjs. Any help is welcome.
I woudlike to run everytime my farm function from my smart contract.
How can I process to run the function every 3 hours ?
There's no native way implemented in the language, because Solidity functions are executed as a result of a transaction. But you can schedule the transaction to be sent every 3 hours.
One of the ways is using a cron initiator of a Chainlink job.
Docs with examples of the job config:
https://docs.chain.link/docs/job-specifications/
https://docs.chain.link/docs/initiators/#cron
Or running a script on your server that sends the transaction directly (without using Chainlink as a mediator).
const Web3 = require('web3');
const web3 = new Web3(providerUrl);
const myContract = new web3.eth.Contract(jsonAbi, contractAddress);
web3.eth.accounts.wallet.add(senderPrivateKey);
async function sendTransaction() {
myContract.methods.myFunction().send({from: senderAddress});
}
setInterval('sendTransaction', 3 * 60 * 60 * 1000); // 3 hours in milliseconds
You can accomplish this type of 'smart contract cron' or 'smart contract scheduler' completely on chain using Chainlink Keepers. I'd recommend following the sample Keepers contract from the Chinalink docs, where you can set a time interval (in your case, every 3 hours). After the 3 hour interval has passed, you can define which smart contract function you want to automate within the performUpkeep function: https://docs.chain.link/docs/chainlink-keepers/compatible-contracts/#example-contract
Once you've written that contract, register it on the Keepers network at keepers.chain.link to begin the automation.
I know the function's name seems to be self explanatory, however, after researching for quite a while I can't find a transaction number anywhere within a clientSession.
Is it an internal number ? is it possible to get it ?
Transaction numbers are used by mongodb to keep track of operations(read/writes) per transaction per session. Sessions can be started either explicitly by calling startSession() or implicity whenever you create a mongodb connection to db server.
How incrementTransactionNumber() works with sessions (explicit)
When you start a session, by calling client.startSession() method, it will create a new ClientSession. This takes in already created server session pool as one of its' constructor parameters. (See) These server sessions have a property called txnNumber which is initialized to be 0.(Init) So whenever you start a transaction by calling startTransaction(), client session object calls incrementTransactionNumber() internally to increment the txnNumber in server session. And all the successive operations will use the same txnNumber, until you call, commitTransaction() or abortTransaction() methods. Reason that you can't find it anywhere within clientSession is, it is a property of serverSession not clientSession.
ServerSession
class ServerSession {
constructor() {
this.id = { id: new Binary(uuidV4(), Binary.SUBTYPE_UUID) };
this.lastUse = now();
this.txnNumber = 0;
this.isDirty = false;
}
So whenever you try to send a command to database (read/write), this txnNumber would be sent along with it. (Assign transaction number to command)
This is to keep track of database operations that belong to a given transaction per session. (A transaction operation history that uniquely identify each transaction per session.)
How incrementTransactionNumber() works with sessions (implicit)
In this case it would be called every time a new command is issued to the database if that command does not belong to a transaction and it's a write operation where retryWrites are enabled. So each new write operation would have new transaction number as long as it does not belong to a explicitly started transaction with startTransaction(). But in this case as well a txnNumber would be sent along with each command.
execute_operation.
const willRetryWrite =
topology.s.options.retryWrites === true &&
session &&
!inTransaction &&
supportsRetryableWrites(server) &&
operation.canRetryWrite;
if (
operation.hasAspect(Aspect.RETRYABLE) &&
((operation.hasAspect(Aspect.READ_OPERATION) && willRetryRead) ||
(operation.hasAspect(Aspect.WRITE_OPERATION) && willRetryWrite))
) {
if (operation.hasAspect(Aspect.WRITE_OPERATION) && willRetryWrite) {
operation.options.willRetryWrite = true;
session.incrementTransactionNumber();
}
operation.execute(server, callbackWithRetry);
return;
}
operation.execute(server, callback);
Also read this article as well. And yes if you need you can get the transaction number for any session through txnNumber property, clientSession.serverSession.txnNumber.
How to use "waiting for external" event functionality of durable task framework in the code. Following is a sample code.
context.ScheduleWithRetry<LicenseActivityResponse>(
typeof(LicensesCreatorActivity),
_retryOptions,
input);
I am using ScheduleWithRetry<> method of context for scheduling my task on DTF but when there is an exception occurring in the code. The above method retries for the _retryOptions number of times.
After completing the retries, the Orchestration status will be marked as Failed.
I need a process by which i can resume my orchestration on DTF after correcting the reason of exception.
I am looking into the githib code for the concerned method in the code but no success.
I have concluded two solution:
Call a framework's method (if exist) and re-queue the orchestration from the state where it failed.
Hold the orchestration code in try catch and in catch section i implement a method CreateOrchestrationInstanceWithRaisedEventAsync whcih will put the orchestration in hold state until an external event triggers it back. Whenever a user (using some front end application) will call the external event for resuming (which means the user have made the corrections which were causing exception).
These are my understandings, if one of the above is possible then kindly guide me through some technical suggestions. otherwise find me a correct path for this task.
For the community's benefit, Salman resolved the issue by doing the following:
"I solved the problem by creating a sub orchestration in case of an exception occurs while performing an activity. The sub orchestration lock the event on azure as pending state and wait for an external event which raise the locked event so that the parent orchestration resumes the process on activity. This process helps if our orchestrations is about to fail on azure durable task framework"
I have figured out the solution for my problem by using "Signal Orchestrations" taken from code from GitHub repository.
Following is the solution diagram for the problem.
In this diagram, before the solution implemented, we only had "Process Activity" which actually executes the activity.
Azure Storage Table is for storing the multiplier values of an instanceId and ActivityName. Why we implemented this will get clear later.
Monitoring Website is the platform from where a user can re-queue/retry the orchestration activity to perform.
Now we have a pre-step and a post-step.
1. Get Retry Option (Pre-Step)
This method basically set the value of RetryOptions instance value.
private RetryOptions ModifyMaxRetires(OrchestrationContext context, string activityName)
{
var failedInstance =
_azureStorageFailedOrchestrationTasks.GetSingleEntity(context.OrchestrationInstance.InstanceId,
activityName);
var configuration = Container.GetInstance<IConfigurationManager>();
if (failedInstance.Result == null)
{
return new RetryOptions(TimeSpan.FromSeconds(configuration.OrderTaskFailureWaitInSeconds),
configuration.OrderTaskMaxRetries);
}
var multiplier = ((FailedOrchestrationEntity)failedInstance.Result).Multiplier;
return new RetryOptions(TimeSpan.FromSeconds(configuration.OrderTaskFailureWaitInSeconds),
configuration.OrderTaskMaxRetries * multiplier);
}
If we have any entry in our azure storage table against the instanceId and ActivityName, we takes the multiplier value from the table and updates the value of retry number in RetryOption instance creation. otherwise we are using the default number of retry value which is coming from our config.
Then:
We process the activity with scheduled retry number (if activity fails in any case).
2. Handle Exceptions (Post-Step)
This method basically handles the exception in case of the activity fails to complete even after the number of retry count set for the activity in RetryOption instance.
private async Task HandleExceptionForSignal(OrchestrationContext context, Exception exception, string activityName)
{
var failedInstance = _azureStorageFailedOrchestrationTasks.GetSingleEntity(context.OrchestrationInstance.InstanceId, activityName);
if (failedInstance.Result != null)
{
_azureStorageFailedOrchestrationTasks.UpdateSingleEntity(context.OrchestrationInstance.InstanceId, activityName, ((FailedOrchestrationEntity)failedInstance.Result).Multiplier + 1);
}
else
{
//const multiplier when first time exception occurs.
const int multiplier = 2;
_azureStorageFailedOrchestrationTasks.InsertActivity(new FailedOrchestrationEntity(context.OrchestrationInstance.InstanceId, activityName)
{
Multiplier = multiplier
});
}
var exceptionInput = new OrderExceptionContext
{
Exception = exception.ToString(),
Message = exception.Message
};
await context.CreateSubOrchestrationInstance<string>(typeof(ProcessFailedOrderOrchestration), $"{context.OrchestrationInstance.InstanceId}_{Guid.NewGuid()}", exceptionInput);
}
The above code first try to find the instanceID and ActivityName in azure storage. If it is not there then we simply add a new row in azure storage table for the InstanceId and ActivityName with the default multiplier value 2.
Later on we creates a new exception type instance for sending the exception message and details to sub-orchestration (which will be shown on monitoring website to a user). The sub-orchestration waits for the external event fired from a user against the InstanceId of the sub-orchestration.
Whenever it is fired from monitoring website, the sub-orchestration will end up and go back to start parent orchestration once again. But this time, when the Pre-Step activity will be called once again it will find the entry in azure storage table with a multiplier. Which means the retry options will get updated after multiplying it with default retry options.
So by this way, we can continue our orchestrations and prevent them from failing.
Following is the class of sub-orchestrations.
internal class ProcessFailedOrderOrchestration : TaskOrchestration<string, OrderExceptionContext>
{
private TaskCompletionSource<string> _resumeHandle;
public override async Task<string> RunTask(OrchestrationContext context, OrderExceptionContext input)
{
await WaitForSignal();
return "Completed";
}
private async Task<string> WaitForSignal()
{
_resumeHandle = new TaskCompletionSource<string>();
var data = await _resumeHandle.Task;
_resumeHandle = null;
return data;
}
public override void OnEvent(OrchestrationContext context, string name, string input)
{
_resumeHandle?.SetResult(input);
}
}
Is it possible to return a value in a transaction method in Hyperledger? such as
/**
* #param {org.n.blockchaindemo.GetCreditScoreUser} GetCreditScoreUser -
the GetCreditScoreUser transaction
* #transaction
*/
async function getCreditScoreUser(user) {
return 0;
}
If this is not possible, would this mean that the return value would have to be put as a property in a participant or asset?
No, you cannot 'return' out of a transaction - you would have to set something else up to 'emit' something of value.
To return something from your transaction to a client, you would either use Events (see example here, eg. emitting a commodity relationship id) or use a call-out (see here for code examples.
Note also we have an Improvement proposal to address returning data to the client app which you can track for progress info etc-> https://github.com/hyperledger/composer/issues/4165