forked from sphinx-labs/sphinx
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
98e4de6
commit 1ce34a9
Showing
24 changed files
with
781 additions
and
170 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
'@sphinx/contracts': minor | ||
'@sphinx/plugins': patch | ||
'@sphinx/core': patch | ||
--- | ||
|
||
Add Balance contracts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.15; | ||
|
||
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; | ||
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
|
||
/** | ||
* @title SphinxBalance | ||
* @notice The SphinxBalance contract is where an organization stores its USDC, which pays for | ||
* deployments in the Sphinx DevOps platform. This contract is only meant to exist on | ||
* Optimism Mainnet and Optimism Goerli. | ||
* | ||
* This contract is owned by a single address, which belongs to the organization. Anyone can | ||
* transfer USDC to this contract, but only the owner can transfer these funds elsewhere. To | ||
* fund a deployment, the owner of this contract sends a transaction that transfers USDC to | ||
* the corresponding SphinxEscrow contract. There is a one-to-one mapping between | ||
* SphinxBalance and SphinxEscrow contracts. Both are deployed by the SphinxBalanceFactory | ||
* contract. | ||
* | ||
* The owner of this contract can also increase or decrease the USDC allowance of an | ||
* arbitrary spender address using the standard ERC20 allowance mechanism. By setting the | ||
* corresponding SphinxEscrow contract as the spender, the owner can fund deployments via | ||
* this allowance mechanism. | ||
* | ||
* Note that we don't need to check the boolean values that are returned from function calls | ||
* to the USDC contract, such as `usdc.transfer`. The is because these functions in the USDC | ||
* contract always return true. | ||
*/ | ||
contract SphinxBalance is Ownable { | ||
ERC20 public immutable usdc; | ||
|
||
address public immutable escrow; | ||
|
||
string public orgId; | ||
|
||
constructor(string memory _orgId, address _owner, address _usdc, address _escrow) { | ||
orgId = _orgId; | ||
usdc = ERC20(_usdc); | ||
escrow = _escrow; | ||
_transferOwnership(_owner); | ||
} | ||
|
||
function transfer(address _to, uint256 _amount) external onlyOwner { | ||
usdc.transfer(_to, _amount); | ||
} | ||
|
||
function increaseAllowance(address _spender, uint256 _addedAmount) external onlyOwner { | ||
usdc.increaseAllowance(_spender, _addedAmount); | ||
} | ||
|
||
function decreaseAllowance(address _spender, uint256 _subtractedAmount) external onlyOwner { | ||
usdc.decreaseAllowance(_spender, _subtractedAmount); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.15; | ||
|
||
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; | ||
import { SphinxBalance } from "./SphinxBalance.sol"; | ||
import { SphinxEscrow } from "./SphinxEscrow.sol"; | ||
|
||
/** | ||
* @title SphinxBalanceFactory | ||
* @notice Allows anyone to deploy a SphinxBalance contract and SphinxEscrow contract for a given | ||
* org ID. These two contracts handle payments in the Sphinx DevOps platform. The addresses | ||
* of both contracts are calculated via Create2 using the org ID as the salt. This contract | ||
* is only meant to exist on Optimism Mainnet and Optimism Goerli. | ||
*/ | ||
contract SphinxBalanceFactory { | ||
event BalanceFactoryDeployment( | ||
string indexed orgIdHash, | ||
address owner, | ||
string orgId, | ||
address caller | ||
); | ||
|
||
address public immutable usdc; | ||
|
||
address public immutable managedService; | ||
|
||
mapping(bytes32 => bool) public isDeployed; | ||
|
||
constructor(address _usdc, address _managedService) { | ||
usdc = _usdc; | ||
managedService = _managedService; | ||
} | ||
|
||
function deploy(string memory _orgId, address _owner) external { | ||
require(_owner != address(0), "SphinxBalanceFactory: owner cannot be address(0)"); | ||
|
||
bytes32 salt = keccak256(abi.encode(_orgId)); | ||
require(!isDeployed[salt], "SphinxBalanceFactory: org id already deployed"); | ||
|
||
isDeployed[salt] = true; | ||
|
||
// Next, we'll deploy the SphinxBalance and SphinxEscrow contracts. We don't need to check | ||
// that they've been deployed at the correct Create2 address because their constructors | ||
// can't revert and because it's not possible for a contract to already exist at the Create2 | ||
// address. | ||
|
||
SphinxEscrow escrow = new SphinxEscrow{ salt: salt }(_orgId, usdc, managedService); | ||
|
||
// Deploy a SphinxBalance contract with this contract as the initial owner. This makes it | ||
// easy to calculate the Create2 address of the SphinxBalance contract off-chain, since we | ||
// don't need to know the owner's address to calculate it. | ||
SphinxBalance balance = new SphinxBalance{ salt: salt }( | ||
_orgId, | ||
address(this), | ||
usdc, | ||
address(escrow) | ||
); | ||
|
||
// Transfer ownership of the SphinxBalance contract to the specified owner. | ||
Ownable(balance).transferOwnership(_owner); | ||
|
||
emit BalanceFactoryDeployment(_orgId, _owner, _orgId, msg.sender); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.15; | ||
|
||
import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol"; | ||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
|
||
/** | ||
* @title SphinxEscrow | ||
* @notice The SphinxEscrow contract receives USDC from its corresponding SphinxBalance contract to | ||
* pay for deployments in the Sphinx DevOps platform. Each organization is meant to have one | ||
* SphinxBalance contract and SphinxEscrow contract. | ||
* | ||
* USDC can only be transferred from this contract by addresses that belong to the Sphinx | ||
* DevOps platform. These addresses can also transfer funds away from any contract that has | ||
* given an allowance to this contract. | ||
* | ||
* This contract is only meant to exist on Optimism Mainnet and Optimism Goerli. | ||
* | ||
* Note that we don't need to check the boolean values that are returned from function calls | ||
* to the USDC contract, such as `usdc.transfer`. The is because these functions in the USDC | ||
* contract always return true. | ||
*/ | ||
contract SphinxEscrow { | ||
bytes32 private constant FUNDER_ROLE = keccak256("FUNDER_ROLE"); | ||
|
||
IERC20 public immutable usdc; | ||
|
||
IAccessControl public immutable managedService; | ||
|
||
string public orgId; | ||
|
||
modifier onlyFunder() { | ||
require( | ||
managedService.hasRole(FUNDER_ROLE, msg.sender), | ||
"SphinxEscrow: caller is not a funder" | ||
); | ||
_; | ||
} | ||
|
||
constructor(string memory _orgId, address _usdc, address _managedService) { | ||
orgId = _orgId; | ||
usdc = IERC20(_usdc); | ||
managedService = IAccessControl(_managedService); | ||
} | ||
|
||
function batchTransfer(address[] memory _to, uint256[] memory _amounts) external onlyFunder { | ||
require(_to.length == _amounts.length, "SphinxEscrow: array length mismatch"); | ||
for (uint256 i = 0; i < _to.length; i++) { | ||
usdc.transfer(_to[i], _amounts[i]); | ||
} | ||
} | ||
|
||
function transferFrom(address _from, address _to, uint256 _amount) external onlyFunder { | ||
usdc.transferFrom(_from, _to, _amount); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.