Tasks

Create task

Inputs

Metadata: string; an uri to locate the json file describing the metadata of this task.

Deadline: uint64; unix time stamp (in seconds) before which the task should be finished. If not finished before the deadline, the task can be refunded by the manager.

Budget: ERC20Transfer[] (tuple(address,uint96)); a list of ERC20 contract addresses and the amount of this token that is up for budget. At task creation this amount of ERC20 tokens will be transfer from the sender of the transaction to an escrow contract. This means the Tasks contract should be approved to spend this amount of ERC20 tokens beforehand.

Manager: address; the address that will have the permission to manage the task, such as approving applicants and reviewing submissions. This allows the funder to give this responsibility to someone else.

Preapprove: PreapprovedApplication[] (tuple(address, Reward[] (tuple(bool, address, uint88))); these addresses will get an approved application on task creation. This can be used to save gas on internal tasks or motivate a contributor to take your task, by making it easier for them to take it.

Outputs

TaskId: uint256; the id of the newly created task. Note: currently Solidity does not give the return value of transactions, you can extract this id from the TaskCreated event in the transaction receipt logs instead.

Code

```solidity
function createTask(
    string calldata _metadata,
    uint64 _deadline,
    ERC20Transfer[] calldata _budget,
    address _manager,
    PreapprovedApplication[] calldata _preapprove
) external returns (uint256 taskId) {
    _ensureNotDisabled();
    taskId = taskCounter++;

    Task storage task = tasks[taskId];
    task.metadata = _metadata;
    task.deadline = _deadline;
    task.budgetCount = uint8(_budget.length);
    Escrow escrow = Escrow(Clones.clone(escrowImplementation));
    escrow.__Escrow_init();
    task.escrow = escrow;
    for (uint8 i; i < uint8(_budget.length); ) {
        _budget[i].tokenContract.transferFrom(
            _msgSender(),
            address(escrow),
            _budget[i].amount
        );
        task.budget[i] = _budget[i];
        unchecked {
            ++i;
        }
    }

    task.manager = _manager;
    task.creator = _msgSender();

    // Default values are already correct (save gas)
    // task.state = TaskState.Open;
    unchecked {
        // Impossible to overflow due to openTasks <= taskCounter
        ++openTasks;
    }

    // Gas optimization
    if (_preapprove.length > 0) {
        task.applicationCount = uint16(_preapprove.length);
        for (uint16 i; i < uint16(_preapprove.length); ) {
            Application storage application = task.applications[i];
            application.applicant = _preapprove[i].applicant;
            application.accepted = true;
            _ensureRewardEndsWithNextToken(_preapprove[i].reward);
            _setRewardBellowBudget(
                task,
                application,
                _preapprove[i].reward
            );

            unchecked {
                ++i;
            }
        }
    }

    emit TaskCreated(
        taskId,
        _metadata,
        _deadline,
        _budget,
        _msgSender(),
        _manager,
        _preapprove
    );
}
```

Apply for task

Inputs

TaskId: uint256; the id of the task that you want to apply for.

Metadata: string; the uri pointing to the json metadata of your application.

Reward: Reward[] (tuple(bool, address, uint88)); the desired reward if you manage to complete the task succesfully.

Outputs

ApplicationId: uint16; the id of the newly created application.

Code

```solidity
function applyForTask(
    uint256 _taskId,
    string calldata _metadata,
    Reward[] calldata _reward
) external returns (uint16 applicationId) {
    _ensureNotDisabled();
    Task storage task = _getTask(_taskId);
    _ensureTaskIsOpen(task);
    _ensureRewardEndsWithNextToken(_reward);

    Application storage application = task.applications[
        task.applicationCount
    ];
    application.metadata = _metadata;
    application.applicant = _msgSender();
    application.rewardCount = uint8(_reward.length);
    for (uint8 i; i < uint8(_reward.length); ) {
        application.reward[i] = _reward[i];
        unchecked {
            ++i;
        }
    }

    applicationId = task.applicationCount++;

    emit ApplicationCreated(
        _taskId,
        applicationId,
        _metadata,
        _reward,
        task.manager,
        _msgSender()
    );
}
```

Accept applications

Inputs

TaskId: uint256; the id of the task you want to accept applications of.

ApplicationIds: uint16; the ids of the applications you want to accept.

Code

```solidity
function acceptApplications(
    uint256 _taskId,
    uint16[] calldata _applicationIds
) external {
    _ensureNotDisabled();
    Task storage task = _getTask(_taskId);
    _ensureTaskIsOpen(task);
    _ensureSenderIsManager(task);

    for (uint i; i < _applicationIds.length; ) {
        _ensureApplicationExists(task, _applicationIds[i]);

        Application storage application = task.applications[
            _applicationIds[i]
        ];
        application.accepted = true;
        _increaseBudgetToReward(
            task,
            application.rewardCount,
            application.reward
        );
        emit ApplicationAccepted(
            _taskId,
            _applicationIds[i],
            _msgSender(),
            application.applicant
        );

        unchecked {
            ++i;
        }
    }
}
```

Take task

Inputs

TaskId: uint256; the id of the task you want to take.

ApplicationId: uint16; the id of your applications, which has been accepted.

Code

```solidity
function takeTask(uint256 _taskId, uint16 _applicationId) external {
    _ensureNotDisabled();
    Task storage task = _getTask(_taskId);
    _ensureTaskIsOpen(task);
    _ensureApplicationExists(task, _applicationId);
    
    Application storage application = task.applications[_applicationId];
    _ensureSenderIsApplicant(application);
    _ensureApplicationIsAccepted(application);
    
    task.executorApplication = _applicationId;
    
    unchecked {
        --openTasks;
        ++takenTasks;
    }
    task.state = TaskState.Taken;
    
    emit TaskTaken(_taskId, _applicationId, task.manager, _msgSender());
}
```

Create submission

Inputs

TaskId: uint256; the id of the task you want to make a submission for.

Metadata: string; the uri pointing to your submission data.

Outputs

SubmissionId: uint8; the id of the newly created submission.

Code

```solidity
function createSubmission(
    uint256 _taskId,
    string calldata _metadata
) external returns (uint8 submissionId) {
    _ensureNotDisabled();
    Task storage task = _getTask(_taskId);
    _ensureTaskIsTaken(task);
    _ensureSenderIsExecutor(task);

    Submission storage submission = task.submissions[task.submissionCount];
    submission.metadata = _metadata;
    submissionId = task.submissionCount++;

    emit SubmissionCreated(
        _taskId,
        submissionId,
        _metadata,
        task.manager,
        _msgSender()
    );
}
```

Review submission

Inputs

TaskId: uint256; the id of the task you want to review a submission of.

SubmissionId: uint8; the id of the submission you want to review.

Judgement: SubmissionJudgement (uint8); if you accept the submission as completion of the task.

Feedback: string; the uri explaining why you made your decision.

Code

```solidity
function reviewSubmission(
    uint256 _taskId,
    uint8 _submissionId,
    SubmissionJudgement _judgement,
    string calldata _feedback
) external {
    _ensureNotDisabled();
    Task storage task = _getTask(_taskId);
    _ensureTaskIsTaken(task);
    _ensureSenderIsManager(task);
    _ensureSubmissionExists(task, _submissionId);

    Submission storage submission = task.submissions[_submissionId];
    _ensureSubmissionNotJudged(submission);
    _ensureJudgementNotNone(_judgement);
    submission.judgement = _judgement;
    submission.feedback = _feedback;

    if (_judgement == SubmissionJudgement.Accepted) {
        unchecked {
            --takenTasks;
            ++successfulTasks;
        }
        _payoutTask(task);

        emit TaskCompleted(
            _taskId,
            _msgSender(),
            task.applications[task.executorApplication].applicant
        );
    }

    emit SubmissionReviewed(
        _taskId,
        _submissionId,
        _judgement,
        _feedback,
        _msgSender(),
        task.applications[task.executorApplication].applicant
    );
}
```

Cancel task

Inputs

TaskId: uint256; the id of the task you wish to cancel.

Explanation: string; the uri to why you would like to cancel this task.

Outputs

CancelTaskRequestId: uint8; in case the task is able to be cancelled instantly, this will return 255. Otherwise it will return the id of the request that has been made.

Code

```solidity
function cancelTask(
    uint256 _taskId,
    string calldata _explanation
) external returns (uint8 cancelTaskRequestId) {
    _ensureNotDisabled();
    Task storage task = _getTask(_taskId);
    _ensureSenderIsManager(task);

    _ensureTaskNotClosed(task);

    if (
        task.state == TaskState.Open ||
        task.deadline <= uint64(block.timestamp)
    ) {
        // Task is open or deadline past
        if (task.state == TaskState.Open) {
            unchecked {
                --openTasks;
            }
        } else {
            // if (task.state == TaskState.Taken) {
            unchecked {
                --takenTasks;
            }
        }
        _refundCreator(task);

        emit TaskCancelled(
            _taskId,
            _msgSender(),
            task.state == TaskState.Open
                ? address(0)
                : task.applications[task.executorApplication].applicant
        );
        // Max means no request
        cancelTaskRequestId = type(uint8).max;
    } else {
        // Task is taken and deadline has not past
        CancelTaskRequest storage request = task.cancelTaskRequests[
            task.cancelTaskRequestCount
        ];
        request.explanation = _explanation;
        cancelTaskRequestId = task.cancelTaskRequestCount++;

        emit CancelTaskRequested(
            _taskId,
            cancelTaskRequestId,
            _explanation,
            _msgSender(),
            task.applications[task.executorApplication].applicant
        );
    }
}
```

Accept request

Inputs

TaskId: uint256; the id of the task you want to accept a request from.

RequestType: RequestType (uint8); the type of request you want to accept.

RequestId: uint8; the id of the request you want to accept.

Execute: bool; if you want to execute the request in this transaction.

Code

```solidity
function acceptRequest(
    uint256 _taskId,
    RequestType _requestType,
    uint8 _requestId,
    bool _execute
) external {
    _ensureNotDisabled();
    Task storage task = _getTask(_taskId);
    _ensureTaskIsTaken(task);
    _ensureSenderIsExecutor(task);

    //if (_requestType == RequestType.CancelTask) {
    {
        _ensureCancelTaskRequestExists(task, _requestId);

        CancelTaskRequest storage cancelTaskRequest = task
            .cancelTaskRequests[_requestId];
        _ensureRequestNotAccepted(cancelTaskRequest.request);

        if (_execute) {
            // use executeRequest in the body instead? (more gas due to all the checks, but less code duplication)
            unchecked {
                --takenTasks;
            }
            _refundCreator(task);

            emit TaskCancelled(_taskId, task.manager, _msgSender());
            cancelTaskRequest.request.executed = true;
        }

        cancelTaskRequest.request.accepted = true;
    }

    emit RequestAccepted(
        _taskId,
        _requestType,
        _requestId,
        task.manager,
        _msgSender()
    );
}
```

Execute request

Inputs

TaskId: uint256; the id of the task you want to execute a request from.

RequestType: RequestType (uint8); the type of request you want to execute.

RequestId: uint8; the id of the accepted request you want to execute.

Code

```solidity
function executeRequest(
    uint256 _taskId,
    RequestType _requestType,
    uint8 _requestId
) external {
    _ensureNotDisabled();
    Task storage task = _getTask(_taskId);
    _ensureTaskIsTaken(task);

    //if (_requestType == RequestType.CancelTask) {
    {
        _ensureCancelTaskRequestExists(task, _requestId);

        CancelTaskRequest storage cancelTaskRequest = task
            .cancelTaskRequests[_requestId];
        _ensureRequestAccepted(cancelTaskRequest.request);
        _ensureRequestNotExecuted(cancelTaskRequest.request);

        unchecked {
            --takenTasks;
        }
        _refundCreator(task);

        emit TaskCancelled(
            _taskId,
            task.manager,
            task.applications[task.executorApplication].applicant
        );
        cancelTaskRequest.request.executed = true;
    }

    emit RequestExecuted(
        _taskId,
        _requestType,
        _requestId,
        _msgSender(),
        task.manager,
        task.applications[task.executorApplication].applicant
    );
}
```

Extend deadline

Inputs

TaskId: uint256; the id of the task you want to extend the deadline of.

Extensions: uint64; how many seconds to increase the deadline by.

Code

```solidity
function extendDeadline(uint256 _taskId, uint64 _extension) external {
    _ensureNotDisabled();
    Task storage task = _getTask(_taskId);
    _ensureSenderIsManager(task);
    
    _ensureTaskNotClosed(task);
    
    task.deadline += _extension;
    
    emit DeadlineExtended(
        _taskId,
        _extension,
        _msgSender(),
        task.state == TaskState.Open
            ? address(0)
            : task.applications[task.executorApplication].applicant
    );
}
```

Increase budget

Inputs

TaskId: uint256; the id of the task you want to increase the budget of.

Increase: uint96[]; the amount to increase the budget by. This array should be the same length as the budget array. Each item will increase the budget entry with the same array index.

Code

```solidity
function increaseBudget(
    uint256 _taskId,
    uint96[] calldata _increase
) external {
    _ensureNotDisabled();
    Task storage task = _getTask(_taskId);
    _ensureSenderIsManager(task);

    _ensureTaskIsOpen(task);

    for (uint8 i; i < uint8(_increase.length); ) {
        ERC20Transfer storage transfer = task.budget[i];
        transfer.tokenContract.transferFrom(
            _msgSender(),
            address(task.escrow),
            _increase[i]
        );
        transfer.amount += _increase[i];

        unchecked {
            ++i;
        }
    }

    emit BudgetIncreased(_taskId, _increase, _msgSender());
}
```

Edit metadata

Inputs

TaskId: uint256; the id of the task you want to edit the metadata of.

NewMetadata: string; the uri of the new metadata of this task.

Code

```solidity
function editMetadata(
    uint256 _taskId,
    string calldata _newMetadata
) external {
    _ensureNotDisabled();
    Task storage task = _getTask(_taskId);
    _ensureSenderIsManager(task);

    _ensureTaskIsOpen(task);

    task.metadata = _newMetadata;
    emit MetadataEditted(_taskId, _newMetadata, _msgSender());
}
```

Last updated