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

Kinto App Registry #28

Merged
merged 48 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
920e9f7
KintoApp
rrecuero Jan 4, 2024
ae08457
Complete functionality
rrecuero Jan 5, 2024
d68e8db
Modifies paymaster to read from sponsored contracts
rrecuero Jan 5, 2024
670ed46
Makes paymaster a bit more resilient
rrecuero Jan 5, 2024
4ef149e
Get gas limits from app registry
rrecuero Jan 5, 2024
d2f8616
Changes in the wallet
rrecuero Jan 5, 2024
78764b6
forge fmt
rrecuero Jan 5, 2024
359c6e6
Merge with latest
rrecuero Jan 5, 2024
7c5fea6
cleanup
rrecuero Jan 6, 2024
1481634
Merge branch 'main' into kintoapps
rrecuero Jan 6, 2024
ac7f7ba
Merge branch 'main' into kintoapps
rrecuero Jan 6, 2024
501e31b
First tests of KintoApp
rrecuero Jan 6, 2024
a735230
mechanism to set the new param in wallet impl
rrecuero Jan 6, 2024
7983d4d
More tests passing
rrecuero Jan 6, 2024
3df9a65
More test fixes
rrecuero Jan 7, 2024
8e638e2
forge format
rrecuero Jan 7, 2024
ea66e1d
All tests passing
rrecuero Jan 7, 2024
7ff1442
Tests for KintoApp
rrecuero Jan 7, 2024
fbcc9cc
Renames app registry
rrecuero Jan 7, 2024
2c0df80
Renames isContractSponsored
rrecuero Jan 7, 2024
077dc3f
Adds events
rrecuero Jan 7, 2024
17cc6a6
Struct packing
rrecuero Jan 7, 2024
5b64e26
Changes AccessControl to ownable in KintoAppRegistry
rrecuero Jan 7, 2024
d972bee
Fixes KYC viewer issue
rrecuero Jan 7, 2024
c686a75
Adds needed migrations
rrecuero Jan 7, 2024
09cba14
Update src/paymasters/SponsorPaymaster.sol
rrecuero Jan 8, 2024
d703a1f
Update src/paymasters/SponsorPaymaster.sol
rrecuero Jan 8, 2024
b8dabf4
Merge with latest
rrecuero Jan 8, 2024
ec08496
Changes compiler version
rrecuero Jan 8, 2024
f5fc112
Update src/apps/KintoAppRegistry.sol
rrecuero Jan 8, 2024
f7072a2
Update test/KintoAppRegistry.t.sol
rrecuero Jan 8, 2024
a2e1334
Addresses comments
rrecuero Jan 8, 2024
e3c2b8e
more comments
rrecuero Jan 8, 2024
ab6a8f2
Adds missing events
rrecuero Jan 8, 2024
59b92f7
broadcast
rrecuero Jan 8, 2024
5be6ae1
comments
rrecuero Jan 8, 2024
cc537c5
Fixes app whitelist and child parent req
rrecuero Jan 8, 2024
5b7d846
refactor: rmv unneeded files
fedealconada Jan 8, 2024
bb63af7
refactor: unify Counter contracts
fedealconada Jan 8, 2024
de4b17f
refactor: rename _kintoApp as _kintoAppRegistry
fedealconada Jan 8, 2024
6e06940
chore: rebase
fedealconada Jan 8, 2024
6a2f601
refactor: some refactors on the tests files and also renamed setAppWh…
fedealconada Jan 8, 2024
352f592
test: add whitelist tests
fedealconada Jan 8, 2024
2b391a7
test: add more tests
fedealconada Jan 9, 2024
6f1ac47
Merge pull request #32 from KintoXYZ/registry-tests
fedealconada Jan 9, 2024
52e7be4
Register app gated by deployer
rrecuero Jan 9, 2024
db286ec
Modifies migration
rrecuero Jan 9, 2024
1f8cd4b
Makes app registry immutable
rrecuero Jan 9, 2024
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
278 changes: 278 additions & 0 deletions src/apps/KintoApp.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;

import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
rrecuero marked this conversation as resolved.
Show resolved Hide resolved
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

import "../interfaces/IKintoID.sol";
import "../interfaces/IKintoApp.sol";
import "../interfaces/IKintoWalletFactory.sol";

// import "forge-std/console2.sol";

/**
* @title KintoApp
* @dev A contract that holds all the information of a KintoApp
*/
contract KintoApp is
rrecuero marked this conversation as resolved.
Show resolved Hide resolved
Initializable,
ERC721Upgradeable,
ERC721EnumerableUpgradeable,
ERC721BurnableUpgradeable,
AccessControlUpgradeable,
UUPSUpgradeable,
IKintoApp
{
/* ============ Constants ============ */
rrecuero marked this conversation as resolved.
Show resolved Hide resolved
bytes32 public constant override UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
bytes32 public constant override DEVELOPER_ADMIN = keccak256("DEVELOPER_ADMIN");

uint256 public constant RATE_LIMIT_PERIOD = 1 minutes;
uint256 public constant RATE_LIMIT_THRESHOLD = 10;
uint256 public constant GAS_LIMIT_PERIOD = 30 days;
uint256 public constant GAS_LIMIT_THRESHOLD = 1e16; // 0.01 ETH

/* ============ State Variables ============ */

uint256 private _nextTokenId;

mapping(address => IKintoApp.Metadata) public appMetadata;
mapping(address => address) public childToParentContract;
mapping(address => mapping(address => bool)) public appSponsoredContracts; // other contracts to be sponsored

/* ============ Events ============ */
rrecuero marked this conversation as resolved.
Show resolved Hide resolved

/* ============ Constructor & Initializers ============ */

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function initialize() external initializer {
__ERC721_init("Kinto APP", "KINTOAPP");
__ERC721Enumerable_init();
__ERC721Burnable_init();
__AccessControl_init();
__UUPSUpgradeable_init();
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(UPGRADER_ROLE, msg.sender);
_grantRole(DEVELOPER_ADMIN, msg.sender);
}

/**
* @dev Authorize the upgrade. Only by the upgrader role.
* @param newImplementation address of the new implementation
*/
// This function is called by the proxy contract when the implementation is upgraded
function _authorizeUpgrade(address newImplementation) internal override onlyRole(UPGRADER_ROLE) {}

/* ============ App Registration ============ */

/**
* @dev Register a new app and mints the NFT to the creator
* @param _name The name of the app
* @param parentContract The address of the parent contract
* @param childContracts The addresses of the child contracts
* @param appLimits The limits of the app
*/
function registerApp(
string calldata _name,
address parentContract,
address[] calldata childContracts,
uint256[4] calldata appLimits
) external override {
require(appLimits.length == 4, "Invalid app limits");
_updateMetadata(_name, parentContract, childContracts, appLimits);
_nextTokenId++;
_safeMint(msg.sender, _nextTokenId);
}
rrecuero marked this conversation as resolved.
Show resolved Hide resolved

/**
* @dev Allows the developer to set sponsored contracts
* @param _app The address of the app
* @param _contracts The addresses of the contracts
* @param _flags The flags of the contracts
*/
function setSponsoredContracts(address _app, address[] calldata _contracts, bool[] calldata _flags)
external
override
{
require(_contracts.length == _flags.length, "Invalid input");
require(msg.sender == appMetadata[_app].developerWallet, "Only developer can set sponsored contracts");
for (uint256 i = 0; i < _contracts.length; i++) {
appSponsoredContracts[_app][_contracts[i]] = _flags[i];
}
}

/**
* @dev Allows the developer to update the metadata of the app
* @param _name The name of the app
* @param parentContract The address of the parent contract
* @param childContracts The addresses of the child contracts
* @param appLimits The limits of the app
*/
function updateMetadata(
string calldata _name,
address parentContract,
address[] calldata childContracts,
uint256[4] calldata appLimits
) external override {
require(appLimits.length == 4, "Invalid app limits");
require(msg.sender == appMetadata[parentContract].developerWallet, "Only developer can update metadata");
_updateMetadata(_name, parentContract, childContracts, appLimits);
}

/**
* @dev Allows the app to request PII data
* @param app The name of the app
*/
function enableDSA(address app) external override onlyRole(DEVELOPER_ADMIN) {
require(appMetadata[app].dsaEnabled == false, "DSA already enabled");
appMetadata[app].dsaEnabled = true;
}

/* ============ App Info Fetching ============ */

/**
* @dev Returns the metadata of the app
* @param _contract The address of the app
* @return The metadata of the app
*/
function getAppMetadata(address _contract) external view override returns (IKintoApp.Metadata memory) {
address finalContract =
childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract;
return appMetadata[finalContract];
}

/**
* @dev Returns the limits of the app
* @param _contract The address of the app
* @return The limits of the app
*/
function getContractLimits(address _contract) external view override returns (uint256[4] memory) {
address finalContract =
childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract;
IKintoApp.Metadata memory metadata = appMetadata[finalContract];
return [
metadata.rateLimitPeriod != 0 ? metadata.rateLimitPeriod : RATE_LIMIT_PERIOD,
metadata.rateLimitNumber != 0 ? metadata.rateLimitNumber : RATE_LIMIT_THRESHOLD,
metadata.gasLimitPeriod != 0 ? metadata.gasLimitPeriod : GAS_LIMIT_PERIOD,
metadata.gasLimitCost != 0 ? metadata.gasLimitPeriod : GAS_LIMIT_THRESHOLD
];
}

/**
* @dev Returns whether a contract is sponsored by an app
* @param _app The address of the app
* @param _contract The address of the contract
* @return bool true or false
*/
function isContractSponsoredByApp(address _app, address _contract) external view override returns (bool) {
rrecuero marked this conversation as resolved.
Show resolved Hide resolved
return _contract == _app || childToParentContract[_contract] == _app || appSponsoredContracts[_app][_contract];
}

/**
* @dev Returns the contract that sponsors a contract
* @param _contract The address of the contract
* @return The address of the contract that sponsors the contract
*/
function getContractSponsor(address _contract) external view override returns (address) {
if (appMetadata[_contract].developerWallet != address(0)) {
return _contract;
}
if (childToParentContract[_contract] != address(0)) {
return childToParentContract[_contract];
}
return _contract;
}

/* ============ Token name, symbol & URI ============ */

/**
* @dev Gets the token name.
* @return string representing the token name
*/
function name() public pure override(ERC721Upgradeable, IKintoApp) returns (string memory) {
return "Kinto APP";
}

/**
* @dev Gets the token symbol.
* @return string representing the token symbol
*/
function symbol() public pure override(ERC721Upgradeable, IKintoApp) returns (string memory) {
return "KINTOAPP";
}

/**
* @dev Returns the base token URI. ID is appended
* @return token URI.
*/
function _baseURI() internal pure override returns (string memory) {
return "https://kinto.xyz/metadata/kintoapp/";
}

/* =========== App metadata params =========== */
function _updateMetadata(
string calldata _name,
address parentContract,
address[] calldata childContracts,
uint256[4] calldata appLimits
) internal {
IKintoApp.Metadata memory metadata = IKintoApp.Metadata({
name: _name,
developerWallet: msg.sender,
dsaEnabled: false,
rateLimitPeriod: appLimits[0],
rateLimitNumber: appLimits[1],
gasLimitPeriod: appLimits[2],
gasLimitCost: appLimits[3]
});
appMetadata[parentContract] = metadata;
for (uint256 i = 0; i < childContracts.length; i++) {
childToParentContract[childContracts[i]] = parentContract;
}
}

/* ============ Disable token transfers ============ */

/**
* @dev Hook that is called before any token transfer. Allow only mints and burns, no transfers.
* @param from source address
* @param to target address
* @param batchSize The first id
*/
function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize)
internal
virtual
override(ERC721Upgradeable, ERC721EnumerableUpgradeable)
{
require(
(from == address(0) && to != address(0)) || (from != address(0) && to == address(0)),
"Only mint or burn transfers are allowed"
);
super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
}

/* ============ Interface ============ */

/**
* @dev Returns whether the contract implements the interface defined by the id
* @param interfaceId id of the interface to be checked.
* @return true if the contract implements the interface defined by the id.
*/
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721Upgradeable, ERC721EnumerableUpgradeable, AccessControlUpgradeable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
56 changes: 56 additions & 0 deletions src/interfaces/IKintoApp.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

interface IKintoApp {
/* ============ Structs ============ */

struct Metadata {
rrecuero marked this conversation as resolved.
Show resolved Hide resolved
string name;
address developerWallet; // the address that deploys the wallet
bool dsaEnabled; // whether or not this application can request PII from users
uint256 rateLimitPeriod;
uint256 rateLimitNumber; // in txs
uint256 gasLimitPeriod;
uint256 gasLimitCost; // in eth
}

/* ============ State Change ============ */

function registerApp(
string calldata _name,
address parentContract,
address[] calldata childContracts,
uint256[4] calldata appLimits
) external;

function enableDSA(address app) external;

function setSponsoredContracts(address _app, address[] calldata _contracts, bool[] calldata _flags) external;

function updateMetadata(
string calldata _name,
address parentContract,
address[] calldata childContracts,
uint256[4] calldata appLimits
) external;

/* ============ Basic Viewers ============ */

function name() external pure returns (string memory);

function symbol() external pure returns (string memory);

function getContractLimits(address _contract) external view returns (uint256[4] memory);

function getAppMetadata(address _contract) external view returns (Metadata memory);

function getContractSponsor(address _contract) external view returns (address);

function isContractSponsoredByApp(address _app, address _contract) external view returns (bool);

/* ============ Constants and attrs ============ */

function DEVELOPER_ADMIN() external view returns (bytes32);

function UPGRADER_ROLE() external view returns (bytes32);
}
9 changes: 3 additions & 6 deletions src/interfaces/IKintoWallet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.13;
import {IEntryPoint} from "@aa/core/BaseAccount.sol";
import {IKintoWalletFactory} from "./IKintoWalletFactory.sol";
import {IKintoID} from "./IKintoID.sol";
import {IKintoApp} from "./IKintoApp.sol";

interface IKintoWallet {
/* ============ Structs ============ */
Expand All @@ -28,10 +29,6 @@ interface IKintoWallet {

function cancelRecovery() external;

function approveTokens(address app, address[] calldata tokens, uint256[] calldata amount) external;

function revokeTokens(address app, address[] calldata tokens) external;

function setAppKey(address app, address signer) external;

function setAppWhitelist(address[] calldata apps, bool[] calldata flags) external;
Expand All @@ -56,12 +53,12 @@ interface IKintoWallet {

function isFunderWhitelisted(address funder) external view returns (bool);

function isTokenApproved(address app, address token) external view returns (uint256);

function appSigner(address app) external view returns (address);

function appWhitelist(address app) external view returns (bool);

function appRegistry() external view returns (IKintoApp);

function signerPolicy() external view returns (uint8);

/* solhint-disable func-name-mixedcase */
Expand Down
6 changes: 6 additions & 0 deletions src/interfaces/ISponsorPaymaster.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {IKintoApp} from "./IKintoApp.sol";

interface ISponsorPaymaster {
/* ============ Structs ============ */

Expand All @@ -15,6 +17,8 @@ interface ISponsorPaymaster {

function initialize(address owner) external;

function setAppRegistry(address _appRegistry) external;

function addDepositFor(address account) external payable;

function withdrawTokensTo(address target, uint256 amount) external;
Expand All @@ -31,6 +35,8 @@ interface ISponsorPaymaster {

function balances(address account) external view returns (uint256 amount);

function appRegistry() external view returns (IKintoApp);

function contractSpent(address account) external view returns (uint256 amount);

function unlockBlock(address account) external view returns (uint256 block);
Expand Down
Loading
Loading