Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support clientReceiver in updatableV2 #290

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/contracts/contracts/SmartInvoiceUpdatable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ contract SmartInvoiceUpdatable is SmartInvoiceEscrow {
* @notice Handles the provided data, decodes it, and initializes necessary contract state variables.
* @param _data The data to be handled and decoded.
*/
function _handleData(bytes calldata _data) internal override {
function _handleData(bytes calldata _data) internal virtual override {
(
address _client,
uint8 _resolverType,
Expand Down
120 changes: 120 additions & 0 deletions packages/contracts/contracts/SmartInvoiceUpdatableV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ISmartInvoiceFactory} from "./interfaces/ISmartInvoiceFactory.sol";
import {SmartInvoiceUpdatable} from "./SmartInvoiceUpdatable.sol";

/// @title SmartInvoiceUpdatableV2
/// @notice An updatable smart invoice escrow contract with embedded arbitration tailored for guild work.
contract SmartInvoiceUpdatableV2 is SmartInvoiceUpdatable {
using SafeERC20 for IERC20;

/// @notice The receiving address for the provider.
address public clientReceiver;

/// @dev Custom errors for more efficient gas usage.
error InvalidClientReceiver();

/// @notice Emitted when the client's receiver address is updated.
/// @param clientReceiver The updated client receiver address.
event UpdatedClientReceiver(address indexed clientReceiver);

/**
* @notice Internal function for updating the client's receiver address.
* @param _clientReceiver The updated client receiver address.
*/
function _updateClientReceiver(address _clientReceiver) internal {
clientReceiver = _clientReceiver;
emit UpdatedClientReceiver(_clientReceiver);
}

/**
* @notice Updates the client's receiver address.
* @param _clientReceiver The updated client receiver address.
*/
function updateClientReceiver(address _clientReceiver) external onlyClient {
if (_clientReceiver == address(0)) revert InvalidClientReceiver();
_updateClientReceiver(_clientReceiver);
}

/**
* @notice Handles the provided data, decodes it, and initializes necessary contract state variables.
* @param _data The data to be handled and decoded.
*/
function _handleData(bytes calldata _data) internal override {
(
address _client,
uint8 _resolverType,
address _resolver,
address _token,
uint256 _terminationTime, // exact termination date in seconds since epoch
bytes32 _details,
address _wrappedNativeToken,
bool _requireVerification,
address _factory,
address _providerReceiver,
address _clientReceiver
) = abi.decode(
_data,
(
address,
uint8,
address,
address,
uint256,
bytes32,
address,
bool,
address,
address,
address
)
);

if (_clientReceiver == address(0)) revert InvalidClientReceiver();
if (_providerReceiver == address(0)) revert InvalidProviderReceiver();
if (_client == address(0)) revert InvalidClient();
if (_resolverType > uint8(ADR.ARBITRATOR)) revert InvalidResolverType();
if (_resolver == address(0)) revert InvalidResolver();
if (_token == address(0)) revert InvalidToken();
if (_terminationTime <= block.timestamp) revert DurationEnded();
if (_terminationTime > block.timestamp + MAX_TERMINATION_TIME)
revert DurationTooLong();
if (_wrappedNativeToken == address(0))
revert InvalidWrappedNativeToken();

uint256 _resolutionRate = ISmartInvoiceFactory(_factory)
.resolutionRateOf(_resolver);
if (_resolutionRate == 0) {
_resolutionRate = 20;
}

client = _client;
resolverType = ADR(_resolverType);
resolver = _resolver;
token = _token;
terminationTime = _terminationTime;
resolutionRate = _resolutionRate;
details = _details;
wrappedNativeToken = _wrappedNativeToken;
providerReceiver = _providerReceiver;
clientReceiver = _clientReceiver;

if (!_requireVerification) emit Verified(client, address(this));
}

/**
* @dev Internal function to withdraw payment to the client's receiver.
* @param _token The address of the token to transfer.
* @param _amount The amount of tokens to transfer.
*/
function _withdrawDeposit(
address _token,
uint256 _amount
) internal virtual override {
IERC20(_token).safeTransfer(clientReceiver, _amount);
}
}
6 changes: 4 additions & 2 deletions packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "0.1.18",
"dependencies": {
"@nomicfoundation/hardhat-toolbox-viem": "^3.0.0",
"@nomicfoundation/hardhat-viem": "^2.0.3",
"@nomicfoundation/hardhat-viem": "^2.0.6",
"@openzeppelin/contracts": "^5.1.0",
"@openzeppelin/contracts-upgradeable": "^5.1.0",
"@types/chai": "4.3.16",
Expand All @@ -13,7 +13,7 @@
"chai": "^4.5.0",
"chai-as-promised": "^7.1.2",
"dotenv": "^16.3.1",
"hardhat": "^2.22.15",
"hardhat": "^2.22.18",
"hardhat-chai-matchers-viem": "^2.0.8",
"hardhat-gas-reporter": "^2.2.1",
"prettier-plugin-solidity": "^1.4.1",
Expand Down Expand Up @@ -57,6 +57,7 @@
"flatten-instant": "hardhat flatten contracts/SmartInvoiceInstant.sol > flat/SmartInvoiceInstant.sol",
"flatten-spoils": "hardhat flatten contracts/SpoilsManager.sol > flat/SpoilsManager.sol",
"flatten-updatable": "hardhat flatten contracts/SmartInvoiceUpdatable.sol > flat/SmartInvoiceUpdatable.sol",
"flatten-updatable-v2": "hardhat flatten contracts/SmartInvoiceUpdatableV2.sol > flat/SmartInvoiceUpdatableV2.sol",
"flatten-zap": "hardhat flatten contracts/SafeSplitsEscrowZap.sol > flat/SafeSplitsEscrowZap.sol",
"format": "prettier --ignore-path .gitignore --write --plugin=prettier-plugin-solidity \"{*,**/*}.{ts,json,md,sol}\"",
"help": "hardhat help",
Expand All @@ -73,6 +74,7 @@
"test-spoils": "hardhat test ./test/SpoilsManager.ts",
"test-spoils-factory": "hardhat test ./test/SpoilsManagerFactory.ts",
"test-updatable": "hardhat test ./test/SmartInvoiceUpdatable.ts",
"test-updatable-v2": "hardhat test ./test/SmartInvoiceUpdatableV2.ts",
"test-zap": "hardhat test ./test/SafeSplitsEscrowZap.ts",
"typecheck": "tsc --noEmit",
"verify-contract": "hardhat run scripts/verify-contract.ts --network"
Expand Down
6 changes: 5 additions & 1 deletion packages/contracts/scripts/add-implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,14 @@ const ESCROW_TYPES: EscrowTypes = {
invoiceType: 'updatable',
contractName: 'SmartInvoiceUpdatable',
},
'updatable-v2': {
invoiceType: 'updatable-v2',
contractName: 'SmartInvoiceUpdatableV2',
},
};

// Select the desired escrow type
const escrowTypeData = ESCROW_TYPES.escrow;
const escrowTypeData = ESCROW_TYPES['updatable-v2'];
const escrowType = toHex(toBytes(escrowTypeData.invoiceType, { size: 32 }));

async function main(): Promise<void> {
Expand Down
7 changes: 6 additions & 1 deletion packages/contracts/scripts/utils/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Hex } from 'viem';

export type InvoiceType = 'escrow' | 'instant' | 'split-escrow' | 'updatable';
export type InvoiceType =
| 'escrow'
| 'instant'
| 'split-escrow'
| 'updatable'
| 'updatable-v2';

export type SpoilsManager = {
factory?: Hex;
Expand Down
Loading