# RIF DeFi Gateway Documentation ## RIF Defi Gateway Protocol Operation The RIF DeFi Gateway serves as a decentralized intermediary protocol that allows [Service Providers](##Glossary) to offer services, connecting DeFi Protocol Service Providers and [Wallet Providers](##Glossary) final users through RIF DeFi Gateway. RIF DeFi Gateway defines a single common interface for each service type. ### Supported Services - [Lending](###Lending-Service) - [Borrowing](###Borrow-Service) ### RIF DeFi Gateway contracts architecture This class diagram represents a RIF DeFi Gateway project wich provides a single common interface for each service type, the project consists of several smart contracts, which are detailed below: ```mermaid classDiagram IGatewayAccessControl <|-- GatewayAccessControl IOwnable <|-- InitializableOwnable OpenZeppelin_ERC1967Proxy <|-- FeeManager OpenZeppelin_UUPSUpgradeable <|-- FeeManagerLogicV1 InitializableOwnable <|-- FeeManagerLogicV1 IFeeManager <|-- FeeManagerLogicV1 FeeManagerStorageV1 <|-- FeeManagerLogicV1 IOwnable <|-- IRIFGateway OpenZeppelin_ERC1967Proxy <|-- RIFGateway IRIFGateway <|-- RIFGatewayLogicV1 OpenZeppelin_UUPSUpgradeable <|-- RIFGatewayLogicV1 InitializableOwnable <|-- RIFGatewayLogicV1 SubscriptionReporter <|-- RIFGatewayLogicV1 RIFGatewayStorageV1 <|-- RIFGatewayLogicV1 ISubscriptionReporter <|-- SubscriptionReporter OpenZeppelin_IERC165 <|-- IService IService <|-- Service OppenZeppelin_Ownable <|-- Service Service <|-- LendingService ILendingService <|-- LendingService Service <|-- BorrowService IBorrowService <|-- BorrowService OppenZeppelin_Ownable <|-- ServiceTypeManager OppenZeppelin_ERC165Storage <|-- ServiceTypeManager IForwarder <|-- SmartWallet OppenZeppelin_ReentrancyGuard <|-- SmartWallet ISmartWalletFactory <|-- SmartWalletFactory class IOwnable{ +transferOwnership(address newOwner) +owner() address } class IGatewayAccessControl{ +changeOwner(address newOwner) +isOwner(address owner) bool +addLowLevelOperator(address lowOperator) +removeLowLevelOperator(address lowOperator) +isLowLevelOperator(address lowOperator) bool +addHighLevelOperator(address highOperator) +removeHighLevelOperator(address highOperator) +isHighLevelOperator(address highOperator) bool +addFinancialOwner(address financialOwner) +removeFinancialOwner(address financialOwner) +isFinancialOwner(address financialOwner) bool +addFinancialOperator(address financialOperator) +removeFinancialOperator(address financialOperator) +isFinancialOperator(address financialOperator) } class InitializableOwnable{ -bool _initialized -address _owner -uint256[10] __gap +initialize() +owner() address -_checkOwner() +transferOwnership } class IFeeManager{ +getBalance(address beneficiary) uint256 +getDebtBalance(address debtor) uint256 +getDebtBalanceFor(address debtor, address beneficiary) uint256 +withdraw(uint256 amount, address +chargeFee(address debtor, address beneficiary) +pay() +payInBehalfOf(address debtor)beneficiary)+transferOwnership(address newOwner) +getGatewayFeesOwner() address +setRIFGateway(IRIFGateway rifGateway) +getRIFGateway() } class FeemanagerLogicV1{ -_authorizeUpgrade(address newImplementation) +initialize() -_pay(address debtor) -_payBeneficiaries(address debtor, uint256 funds) uint256 -_payOwner(address debtor, uint256 funds) uint256 } class FeeManagerStorageV1 { -uint256 _fixedOwnerFee -uint256 _fixedBeneficiaryFee -address feesOwner -IRIFGateway _rifGateway -mapping(address => address[]) _creditors -mapping(bytes32 => uint256) _amounts mapping(address => uint256) _funds } class IRIFGateway { +addService(Service service) +getServicesAndProviders() (Service[], Provider[]) +requestValidation(address provider) +validateProvider(address provider) +removeService(Service service) +getAccessControl() IGatewayAccessControl } class ISubscriptionReporter { +subscribe(address subscriber, address service, uint256 listingId, address wallet) +getSubscriptions(address subscriber) Subscription[] +feeManager() IFeeManager } class RIFGatewayLogicV1 { -_authorizeUpgrade(address newImplementation) +initialize(address serviceTypeManagerAddr, address gatewayAccessControlAddr, address feeManagerAddr) -_addProviderIfNotExists(address provider) -_checkIfProviderIsAlreadyValidated(address provider) } class RIFGatewayStorageV1 { -Provider[] _providers -mapping(address => uint256) _providerIndexes -ServiceTypeManager _serviceTypeManager -IGatewayAccessControl _accessControl -Service[] _allServices -mapping(address => bool) _uniqueServices -bytes4 _INTERFACE_ID_ERC165 = 0x01ffc9a7 } class SubscriptionReporter{ +mapping(address => Subscription[]) subscriptions +IFeeManager feeManager -uint256[10] __gap +initialize(address feeManagerAddr) } class IService { +addListing(ServiceListing listing) uint256 +disableListing(uint256 listingId) +getListing(uint256 listingId) ServiceListing +getListingsCount() uint256 +updateListing(ServiceListing listing) +getBalance(address currency) uint256 +currentLiquidity(uint256 listingId) uint256 +addLiquidity(uint256 amount, uint256 listingId) +removeLiquidity(uint256 amount, uint256 listingId) +serviceProviderName() string +serviceType() bytes4 } class Service { +bytes4 serviceType +string serviceProviderName +mapping(uint256 => ServiceListing) listings -uint256 _listingCounter -address _rifGateway -_onlyValidListingArgs(uint256 listingId, uint256 amount) -_addLiquidityInternal(uint256 amount, uint256 listingId) -_removeLiquidityInternal(uint256 amount, uint256 listingId) } class ILendingService { +lend(IForwarder.MetaTransaction mtx, uint256 amount, uint256 listingId, address wallet) +withdraw(IForwarder.MetaTransaction mtx, uint256 listingId) } class IBorrowService{ +borrow(IForwarder.MetaTransaction mtx, uint256 amount, uint256 duration, uint256 listingId, address wallet) +pay(IForwarder.MetaTransaction mtx, uint256 amount, uint256 listingId) +withdraw(IForwarder.MetaTransaction mtx, uint256 listingId) +calculateRequiredCollateral(uint256 listingId, uint256 amountToBorrow) uint256 +getCollateralBalance(uint256 listingId) uint256 } class ServiceTypeManager { +addServiceType(bytes4 newServiceInterfaceId) } class IForwarder { +nonce() uint256 +verify(ForwardRequest forwardRequest, bytes signature) +execute(IForwarder.MetaTransaction mtx, bytes data, address to, address currency) (bool, bytes) } class ISmartWalletFactory { +createUserSmartWallet(address owner) +getSmartWalletAddress(address owner) address +getSmartWallet(address owner) SmartWallet } class RSKAddrValidator { +checkPKNotZero(address addr) bool +safeEquals(address addr1, address addr2) bool } class SmartWallet { +uint256 nonce +bytes32 DATA_VERSION_HASH +bytes32 domainSeparator -uint256 _currentBlockForNonce -_buildDomainSeparator() +getDomainSeparator() bytes32 -_encodedDomainSeparator() bytes32 -_setOwner(address owner) -_getOwner() bytes32 -_forwardTokensIfAny(address to, address currency) bool -_getChainID() uint256 -_verifyNonce(ForwardRequest req) -_verifySig(ForwardRequest req, bytes sig) -_getEncoded(ForwardRequest req) +receive() } class SmartWalletFactory { -_getBytecode(address owner) bytes -_deploy(address owner, bytes32 salt) address } ``` * `IOwnable`: An interface that defines the methods for transferring ownership of a contract, as well as getting the current owner's address. * `IGatewayAccessControl`: An interface that defines the methods for managing access control to a contract. It includes methods for changing the contract's owner, adding and removing low-level and high-level operators, as well as financial owners and operators * `InitializableOwnable`: A contract that implements the IOwnable interface and adds functionality for initializing the contract's owner. * `IFeeManager`: An interface that defines the methods for managing fees in a contract. It includes methods for getting balance and debt balance of beneficiaries and debtors, withdrawing funds, charging fees, and paying beneficiaries and owners. * `FeeManagerLogicV1`: A contract that implements the IFeeManager interface and includes logic for paying beneficiaries and owners, as well as upgrading to a new implementation. * `FeeManagerStorageV1`: A contract that stores data for the FeeManagerLogicV1 contract, including fixed fees, fees owner, RIF gateway, creditors, amounts, and funds. * `IRIFGateway`: An interface that defines the methods for managing a RIF gateway contract. It includes methods for adding and removing services, requesting and validating providers, and getting the access control. * `RIFGatewayLogicV1`: A contract that implements the IRIFGateway interface and includes logic for adding and validating providers, as well as upgrading to a new implementation. * `RIFGatewayStorageV1`: A contract that stores data for the `RIFGatewayLogicV1` contract, including providers, service type manager, gateway access control, and an array of all services. * `ISubscriptionReporter`: An interface that defines a set of functions for reporting subscriptions. It includes functions for subscribing to a service, getting subscriptions for an address, and getting the fee manager. * `SubscriptionReporter`: A contract that implements the ISubscriptionReporter interface. It includes mappings for subscriptions and a feeManager variable. * `IService`: An interface that defines the functions for a service contract. It includes methods to add a listing, disable a listing, get a listing, get the number of listings, and update a listing. * `Service`: a contract that implements the IService interface and stores service type, service provider name, listings, listing counter and rif gateway. * `LendingService`: extends Service and implements ILendingService. LendingService is a type of service that provides lending functionality. * `BorrowService`: extends Service and implements IBorrowService. BorrowService is a type of service that provides borrowing functionality. * `ServiceTypeManager`: extends OppenZeppelin_Ownable and OppenZeppelin_ERC165Storage. ServiceTypeManager provides functionality for managing service types. #### Structs ```mermaid classDiagram class Subscription { +address service +uint256 listingId } class ServiceListing { +uint256 id +uint256 minAmount +uint256 maxAmount +uint256 minDuration +uint256 maxDuration +uint256 interestRate +address collateralCurrency +address currency +PayBackOption payBackOption +bool enabled +string name +address owner } class Provider { +address provider +bool validated } class ForwardRequest { +address from +uint256 nonce +address executor } class MetaTransaction { +ForwardRequest req +bytes sig } ``` ## Wallet Provider Integration The role of the wallet provider is to list all the services from the RIF DeFi Gateway and consume them through the single common interface given for the service type. ### Operations #### Retrieve Services ```mermaid sequenceDiagram actor User as Everyday DeFi User participant WP as Wallet Provider participant RDG as RIF DeFi Gateway participant SP as Service Provider User->>WP: List services WP->>RDG: getServicesAndProviders() RDG-->>WP: List of services addresses and providers WP->>User: List and filter allowed service providers ``` #### Lending Services ```mermaid sequenceDiagram actor User as Everyday DeFi User participant WP as Wallet Provider participant RDG as RIF DeFi Gateway participant SP as Service Provider alt Lending Service Consumption User->>WP: Consume lending service WP->>SP: Execute "Lend" function on behalf o the user SP->>RDG: Notify that a service was consumed RDG->>FM: Account for execution fees SP->>SP: Emits lending consumption event else Lending Service: Check Status User->>WP: Check lending service status WP->>SP: Execute "getBalance" function on behalf o the user SP-->>WP: Returns current user's balance WP-->>User: Display current balance else Lending Service: Withdraw User->>WP: Withdraws tokens from the lending service WP->>SP: Execute "withdraw" function on behalf o the user SP->>SP: Emits withdraw event end ``` #### Borrowing Services ```mermaid sequenceDiagram actor User as Everyday DeFi User participant WP as Wallet Provider participant RDG as RIF DeFi Gateway participant SP as Service Provider alt Lending Service Consumption User->>WP: Consume lending service WP->>SP: Execute "Borrow" function on behalf o the user SP->>RDG: Notify that a service was consumed RDG->>FM: Account for execution fees SP->>SP: Emits lending consumption event else Lending Service: Check Status User->>WP: Check lending service status WP->>SP: Execute "getBalance" function on behalf o the user SP-->>WP: Returns current user's balance WP-->>User: Display current balance else Lending Service: Pay User->>WP: Pays tokens to the lending service WP->>SP: Execute "pay" function on behalf of the user SP->>SP: Emits pay event else Lending Service: Withdraw User->>WP: Withdraws tokens from the lending service WP->>SP: Execute "withdraw" function on behalf of the user SP->>SP: Emits withdraw event end ``` ### Service consumption The RIF DeFi Gateway uses intermerdiary an intermediary [Smart Wallet](#Smart-Wallet) for communicating users (EOA accounts) to service providers (Smart Contracts), in order to enable this functionality the Wallet Provider needs to sign a [Meta Transaction](#Meta-Transaction-Signing-EIP-712) or envelope transaction that allows smart contracts to execute transaction on behalf of the users. Steps to sign a Meta Transaction: 1. Obtain the executor address from service provider list retrieved through the DeFi Gateway. 2. Collect the EOA address of the user. 3. Use the Smart Wallet Factory address to connect to the factory and retrieve the user's Smart Wallet address. 4. Use the user's Smart Wallet address to retrieve the current nonce. 5. Process the sign process as described in [Meta Transaction Signing](#Meta-Transaction-Signing-EIP-712) 6. Call the required function to interact with the service provider. ### Retrieving previous consumptions The DeFi Gateway acts as a [Subscription Reporter](https://github.com/rsksmart/rif-everyday-gateway/blob/main/contracts/gateway/SubscriptionReporter.sol) meaning that it provides a single entry point to retrieve past consumptions to service providers. Steps to retrieve previous consumptions: 1. Instantiate a new Contract using the ISubscriptionReporter interface and the DeFi Gateway address. 2. Call the `getSubscriptions` function which returns a list of service providers addresses and listing ids that the user has consumed. 3. To get the status of the service, instantiate the Service Provider using the IService interface and the service provider address. 4. Call the `getBalance` function using the listingId which returns the current balance of the service. **NOTE:** If the retrived balance is equals to 0 the service is no longer active. ## Service Provider Integration The Service Provider is able to add their services into de DeFi RIF Gateway, in order to do this you need to deploy a contract that implements services defined on RIF DeFi Gateway, such as [`LendingService`](https://github.com/rsksmart/rif-everyday-gateway/blob/main/contracts/services/LendingService.sol) or [`BorrowService`](https://github.com/rsksmart/rif-everyday-gateway/blob/main/contracts/services/BorrowService.sol). To see an example implementation of Tropykus Protocol refer to [here](https://github.com/rsksmart/rif-everyday-gateway/tree/main/contracts/tropykus). ### Lending Service: The [Lending Service](https://github.com/rsksmart/rif-everyday-gateway/blob/main/contracts/services/LendingService.sol) is a smart contract that must be implemented by service providers in order to be listed on RIF DeFi Gateway. This service allows Everyday DeFi Users to lend their digital assets and earn interest from Service Providers through the RIF DeFi Gateway. The main functions on this contract include lending tokens, withdrawing tokens plus interest earned, and checking the balance of a lend for a given token. ```solidity function lend( IForwarder.MetaTransaction calldata mtx, uint256 amount, uint256 listingId, address wallet ) external payable; function withdraw( IForwarder.MetaTransaction calldata mtx, uint256 listingId ) external; function getBalance( address currency ) external view returns (uint256); ``` - **`lend`**: Allows users to lend a specified amount of a digital asset to a listing identified by its listing ID. This function requires a MetaTransaction object that includes the necessary parameters for the transaction to be executed. Additionally, the function requires the amount to be lent, the listing ID, and the user's wallet address. - **`withdraw`**: Allows users to withdraw their lent assets from a listing identified by its listing ID. This function requires a MetaTransaction object that includes the necessary parameters for the transaction to be executed. Additionally, the function requires the listing ID. - **`getBalance`**: Allows users to check the balance of a specified digital asset in the Lending Service. Refer to [`TropykusLendingService.sol`](https://github.com/rsksmart/rif-everyday-gateway/blob/main/contracts/tropykus/TropykusLendingService.sol) as an example of how to implement these functions for your lending service. Once your lending service has been implemented, you can call `addService(Service service)` on `RIFGateway` contract, to add your service on the RIF DeFi Gateway, now you can use your service to list your lending offers. See an example bellow: ```javascript await ( await tropykusLendingService.addListing({ minAmount: ethers.utils.parseEther('0.0001'), maxAmount: ethers.utils.parseEther('0.5'), minDuration: 0, maxDuration: 10000, interestRate: ethers.utils.parseEther('0.05'), // 5% collateralCurrency: ethers.constants.AddressZero, currency: ethers.constants.AddressZero, // RBTC payBackOption: PaybackOption.Day, enabled: true, name: 'Tropykus RBTC Lending Service', owner: owner.address, }) ).wait(); ``` ### Borrow Service: The [Borrow Service](https://github.com/rsksmart/rif-everyday-gateway/blob/main/contracts/services/BorrowService.sol) is a smart contract that must be implemented by service providers in order to be add to RIF DeFi Gateway. This service allows Everyday DeFi Users to borrow tokens from a Service Provider through the RIF DeFi Gateway. The main functions on this contract include borrowing tokens, paying back borrowed tokens, withdrawing tokens that where used as collateral, calculating the required collateral for a borrow, checking the balance of collateral tokens, and checking the current debt. ```solidity function borrow( IForwarder.MetaTransaction calldata mtx, uint256 amount, uint256 duration, uint256 listingId, address wallet ) public payable; function pay( IForwarder.MetaTransaction calldata mtx, uint256 amount, uint256 listingId ) public payable; function withdraw( IForwarder.MetaTransaction calldata mtx, uint256 listingId ) public; function calculateRequiredCollateral( uint256 listingId, uint256 amountToBorrow ) external view returns (uint256 amountToLend); function getBalance( address currency ) external view returns (uint256); function getCollateralBalance( uint256 listingId ) external view returns (uint256); ``` * **`borrow`:** Allows users to borrow tokens from the Service Provider. To execute this function, the user must provide a valid MetaTransaction, specify the amount and duration of the loan, the listing ID and the wallet address where the borrowed tokens will be deposited. * **`pay`:** Allows users to pay back the borrowed tokens to the Service Provider. To execute this function, the user must provide a valid MetaTransaction, specifying the amount to be paid and the listing ID. * **`withdraw`:** Allows users to withdraw their deposited tokens from the Service Provider. To execute this function, the user must provide a valid MetaTransaction and the listing ID. * **`calculateRequiredCollateral`:** Allows users to calculate the required collateral for the loan. This function takes the listing ID and the amount to be borrowed as input and returns the required amount of collateral. * **`getBalance`:** Allows users to check their balance with the Service Provider for a given currency. * **`getCollateralBalance`:** Allows users to check their collateral balance with the Service Provider for a given listing Id. Refer to the [`TropykusBorrowingService.sol`](https://github.com/rsksmart/rif-everyday-gateway/blob/main/contracts/tropykus/TropykusBorrowingService.sol) as an example of how to implement these functions for your borrowing service. Once your borrowing service has been implemented, you can call `addService(Service service)` on `RIFGateway` contract, to add your service on the RIF DeFi Gateway, now you can use your service to list your borrowing offers. See an example bellow: ```javascript await ( await tropykusBorrowingService.addListing({ minAmount: ethers.utils.parseEther('1'), maxAmount: ethers.utils.parseEther('100'), minDuration: 0, maxDuration: 1000, interestRate: ethers.utils.parseEther('0.01'), // 1% collateralCurrency: RIFTokenAddress, currency: RDOCTokenAddress, payBackOption: PaybackOption.Day, enabled: true, name: 'Tropykus RDOC Borrow Service', owner: owner.address, }) ).wait(); ``` ### Smart Wallet Given the limitation of having a single common interface for all the service providers the RIF DeFi Gateway uses an intermediary smart wallet between users and service providers, that enables to relay and execute transactions on behalf of the EOA account. The smart wallet supports executing transactions on behalf of the user only if the user has signed a `MetaTransaction` that authorizes an `Executor` to execute operations from within the smart wallet. Inner workings of the message signing and nonce verification process: 1. A smart wallet is created for the user using `createUserSmartWallet` function of `SmartWalletFactory` contract. 2. The user then signs a message using their external wallet. The message consists of a `suffixData`, `req`, and `sig`. The `req` contains the transaction data such as the `to` address, value, and `data`. The `sig` is the signature of the message. 3. The signed message is then verified by the smart wallet using the `verify` function. The `verify` function takes in the `req`, and `sig`. It checks that the message was signed by the user's external wallet and that the nonce of the message is correct. If the verification succeeds, the transaction is executed on the network using the execute function. 4. The `nonce` is a unique number that is used to prevent replay attacks. Each time a transaction is executed, the nonce is incremented. If a transaction with a lower nonce is received after a transaction with a higher nonce, it will be rejected. If a transaction with the same nonce is received twice, it will also be rejected. 5. To generate the salt, the computeSalt function is used. The computeSalt function takes in the user's address and the address of the SmartWalletFactory contract. It returns the keccak256 hash of the user's address, the SmartWalletFactory address, and a string "0". 6. Finally, the domainSeparatorType, eIP712DomainType, and forwardRequestType objects are used to generate the domain separator and message types for the EIP712 standard. The domain separator is a unique string that is used to differentiate between different domains. The message types are used to define the structure of the signed message. ```solidity struct MetaTransaction { ForwardRequest req; bytes sig; } struct ForwardRequest { address from; uint256 nonce; address executor; } ```` ### Supported functions - **nonce**: Returns the current execution nonce - **verify**: Checks if the given signature is valid. - **execute**: Executes a transaction on behalf of the user. ### Meta Transaction Signing (EIP-712) Refer to: https://eips.ethereum.org/EIPS/eip-712 The message to sign must have the following structure: ```typescript type EIP712Payload = { types: { ForwardRequest: Array<{ name: string; type: string }>; }; domain: { name: string; version: string; chainId: string; verifyingContract: string; }; primaryType: string; message: { from: string; nonce: string; executor: string; }; }; ``` Using the constants: ```typescript ForwardRequest = [ { name: 'from', type: 'address' }, { name: 'nonce', type: 'uint256' }, { name: 'executor', type: 'address' }, ] name = 'RSK RIF GATEWAY', version = '1' primaryType`: 'ForwardRequest' ``` For example: ```typescript type EIP712Payload = { types: { ForwardRequest: [ { name: 'from', type: 'address' }, { name: 'nonce', type: 'uint256' }, { name: 'executor', type: 'address' }, ], }, domain: { name: 'RSK RIF GATEWAY', version: '1', chainId: '31', verifyingContract: '0x...', }, primaryType: 'ForwardRequest', message: { from: '0x...', nonce: '0', executor: '0x...', }; }; ``` Reference implementation of a `DeFiGatewaySigner` class used to sign EIP-712 payloads: https://github.com/rsksmart/rif-everyday-gateway/blob/main/test/utils/DeFiGatewaySigner.ts ## Glossary: - **Wallet Provider:** An application that enables users to interact with web3 applications, i.g. create transactions in the RSK blockchain for transfering RBTC, execute smart contracts functions, send tokens, etc. - **Service Provider:** Protocols compatible with the RSK Blockchain that allow users to consume DeFi services. i.g. Lending and borrowing protocols. - **Meta Transaction:** Refers to an enveloped transaction that contains the parameters of the transaction that is going to be relayed to the network and the signature of the Meta Transaction that consists of the signed hashed parameters of the actual transaction.