Skip to content
Merged
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "riff/lib/forge-std"]
path = riff/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "riff/lib/solmate"]
path = riff/lib/solmate
url = https://github.com/Rari-Capital/solmate
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ Each project in this repo lives in its own directory and includes a dedicated RE

Below is a quick summary of each prototype currently available in this repository:

1. **`Project 1 here`**
Description here.
1. **`Riff`**
A bonding curve that you can hear.
2. **`Project 2 here`**
Description here.

Expand Down
14 changes: 14 additions & 0 deletions riff/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Compiler files
cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/

# Docs
docs/

# Dotenv file
.env
10 changes: 10 additions & 0 deletions riff/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# RIFF

## Problem
Trading experience on every Automated Market Maker (AMM) is the same, it's time for something radically different like Riff.

## Insight
What if we used other senses like hearing to make calls?

## Solution
Encrypt a bonding curve to create an asset with a price that no one can see. Let an AI violin be the only party that can see the price. The violin generates music whenever it sees price fluctuations. Now, instead of seeing a price chart, users need to listen to it.
6 changes: 6 additions & 0 deletions riff/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
1 change: 1 addition & 0 deletions riff/lib/forge-std
Submodule forge-std added at 3b20d6
1 change: 1 addition & 0 deletions riff/lib/solmate
Submodule solmate added at c93f77
149 changes: 149 additions & 0 deletions riff/src/Riff.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* SPDX-License-Identifier: UNLICENSED
*
* AMM that hides the price of quote asset until it's above some threshold.
*
*/
pragma solidity ^0.8.13;

import "solmate/tokens/ERC20.sol";
import "solmate/utils/FixedPointMathLib.sol";
import "solmate/utils/ReentrancyGuard.sol";

import "./ViolinCoin.sol";

/*//////////////////////////////////////////////////////////////
// ViolinAMM Contract
//////////////////////////////////////////////////////////////*/

contract Riff is ReentrancyGuard {
/*//////////////////////////////////////////////////////////////
// TOKEN STORAGE
//////////////////////////////////////////////////////////////*/
ViolinCoin public baseAsset;
ViolinCoin public quoteAsset;

/*//////////////////////////////////////////////////////////////
// AMM STORAGE
//////////////////////////////////////////////////////////////*/
saddress adminAddress;

saddress violinAddress;

// Fixed point arithmetic unit
suint256 wad;

// Since the reserves are encrypted, people can't access
// the price information until they swap
suint256 baseReserve;
suint256 quoteReserve;

/*//////////////////////////////////////////////////////////////
// MODIFIERS
//////////////////////////////////////////////////////////////*/

/*
* Only off-chain violin can call this function
*/
modifier onlyViolinListener() {
require(saddress(msg.sender) == violinAddress, "You don't have violin access");
_;
}

/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
ViolinCoin _baseAsset,
ViolinCoin _quoteAsset,
uint256 _wad,
address _adminAddress,
address _violinAddress
) {
baseAsset = _baseAsset;
quoteAsset = _quoteAsset;

adminAddress = saddress(_adminAddress);
violinAddress = saddress(_violinAddress);

// Stored as suint256 for convenience. Not actually shielded bc it's a
// transparent parameter in the constructor
wad = suint256(_wad);
}
/*//////////////////////////////////////////////////////////////
AMM LOGIC
//////////////////////////////////////////////////////////////*/

/*
* Add liquidity to pool. No LP rewards in this implementation.
*/
function addLiquidity(suint256 baseAmount, suint256 quoteAmount) external {
baseReserve = baseReserve + baseAmount;
quoteReserve = quoteReserve + quoteAmount;

saddress ssender = saddress(msg.sender);
saddress sthis = saddress(address(this));
baseAsset.transferFrom(ssender, sthis, baseAmount);
quoteAsset.transferFrom(ssender, sthis, quoteAmount);
}

/*
* Wrapper around swap so calldata for trade looks the same regardless of
* direction.
*/
function swap(suint256 baseIn, suint256 quoteIn) public nonReentrant {
// After listening to the music, the swapper can call this function to swap the assets,
// then the price gets revealed to the swapper

suint256 baseOut;
suint256 quoteOut;

(baseOut, baseReserve, quoteReserve) = _swap(baseAsset, quoteAsset, baseReserve, quoteReserve, baseIn);
(quoteOut, quoteReserve, baseReserve) = _swap(quoteAsset, baseAsset, quoteReserve, baseReserve, quoteIn);
}

/*
* Swap for cfAMM. No fees.
*/
function _swap(ViolinCoin tokenIn, ViolinCoin tokenOut, suint256 reserveIn, suint256 reserveOut, suint256 amountIn)
internal
returns (suint256 amountOut, suint256 reserveInNew, suint256 reserveOutNew)
{
suint256 numerator = mulDivDown(reserveOut, amountIn, wad);
suint256 denominator = reserveIn + amountIn;
amountOut = mulDivDown(numerator, wad, denominator);

reserveInNew = reserveIn + amountIn;
reserveOutNew = reserveOut - amountOut;

saddress ssender = saddress(msg.sender);
saddress sthis = saddress(address(this));
tokenIn.transferFrom(ssender, sthis, amountIn);
tokenOut.transfer(ssender, amountOut);
}

/*
* Returns price of quote asset.
*/
function getPrice() external view onlyViolinListener returns (uint256 price) {
return uint256(_computePrice());
}

/*
* Compute price of quote asset.
*/
function _computePrice() internal view returns (suint256 price) {
price = mulDivDown(baseReserve, wad, quoteReserve);
}

/*
* For wad math.
*/
function mulDivDown(suint256 x, suint256 y, suint256 denominator) internal pure returns (suint256 z) {
require(
denominator != suint256(0) && (y == suint256(0) || x <= suint256(type(uint256).max) / y),
"Overflow or division by zero"
);
z = (x * y) / denominator;
}
}
103 changes: 103 additions & 0 deletions riff/src/SRC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.13;

/*//////////////////////////////////////////////////////////////
// ISRC20 Interface
//////////////////////////////////////////////////////////////*/

interface ISRC20 {
/*//////////////////////////////////////////////////////////////
METADATA FUNCTIONS
//////////////////////////////////////////////////////////////*/
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);

/*//////////////////////////////////////////////////////////////
ERC20 FUNCTIONS
//////////////////////////////////////////////////////////////*/
function balanceOf() external view returns (uint256);
function approve(saddress spender, suint256 amount) external returns (bool);
function transfer(saddress to, suint256 amount) external returns (bool);
function transferFrom(saddress from, saddress to, suint256 amount) external returns (bool);
}

/*//////////////////////////////////////////////////////////////
// SRC20 Contract
//////////////////////////////////////////////////////////////*/

abstract contract SRC20 is ISRC20 {
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;

/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
// All storage variables that will be mutated must be confidential to
// preserve functional privacy.
suint256 internal totalSupply;
mapping(saddress => suint256) internal balance;
mapping(saddress => mapping(saddress => suint256)) internal allowance;

/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(string memory _name, string memory _symbol, uint8 _decimals) {
name = _name;
symbol = _symbol;
decimals = _decimals;
}

/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function balanceOf() public view virtual returns (uint256) {
return uint256(balance[saddress(msg.sender)]);
}

function approve(saddress spender, suint256 amount) public virtual returns (bool) {
allowance[saddress(msg.sender)][spender] = amount;
return true;
}

function transfer(saddress to, suint256 amount) public virtual returns (bool) {
// msg.sender is public information, casting to saddress below doesn't change this
balance[saddress(msg.sender)] -= amount;
unchecked {
balance[to] += amount;
}
return true;
}

function transferFrom(saddress from, saddress to, suint256 amount) public virtual returns (bool) {
suint256 allowed = allowance[from][saddress(msg.sender)]; // Saves gas for limited approvals.
if (allowed != suint256(type(uint256).max)) {
allowance[from][saddress(msg.sender)] = allowed - amount;
}

balance[from] -= amount;
unchecked {
balance[to] += amount;
}
return true;
}

/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(saddress to, suint256 amount) internal virtual {
totalSupply += amount;
unchecked {
balance[to] += amount;
}
}

function _burn(saddress to, suint256 amount) internal virtual {
totalSupply -= amount;
balance[to] -= amount;
}
}
51 changes: 51 additions & 0 deletions riff/src/ViolinCoin.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.13;

import {SRC20, ISRC20} from "./SRC20.sol";

/*//////////////////////////////////////////////////////////////
// IViolinCoin Interface
//////////////////////////////////////////////////////////////*/

// IViolinCoin extends ISRC20 by adding the mint function.
interface IViolinCoin is ISRC20 {
function mint(saddress to, suint256 amount) external;
}
/*//////////////////////////////////////////////////////////////
// ViolinCoin Contract
//////////////////////////////////////////////////////////////*/

contract ViolinCoin is SRC20, IViolinCoin {
address public owner;

constructor(address _owner, string memory _name, string memory _symbol, uint8 _decimals)
SRC20(_name, _symbol, _decimals)
{
owner = _owner;
}

modifier onlyOwner() {
require(msg.sender == owner, "Must be owner");
_;
}

/// @notice Mints new tokens to the specified address.
function mint(saddress to, suint256 amount) public onlyOwner {
_mint(to, amount);
}

/// @notice Returns the balance of msg.sender.
function balanceOf() public view override(ISRC20, SRC20) returns (uint256) {
return super.balanceOf();
}

/// @notice Transfers tokens to another address.
function transfer(saddress to, suint256 amount) public override(ISRC20, SRC20) returns (bool) {
return super.transfer(to, amount);
}

/// @notice Transfers tokens from one address to another.
function transferFrom(saddress from, saddress to, suint256 amount) public override(ISRC20, SRC20) returns (bool) {
return super.transferFrom(from, to, amount);
}
}
Loading