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

Bridged supertoken #26

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
Draft
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,26 @@ The PureSuperTokenDeployer is a _factory_ contract that deploys, upgrades, and
initializes the PureSuperToken in a single transaction. This makes it easy to deploy super tokens from a UI.

---

## alternative-logic

This directory contains modified versions of the actual SuperToken logic.
Unlike the other contracts, here the added/changed functionality isn't applied to the proxy itself, but to an alternative version of the SuperToken logic contract.
This allows existing SuperTokens to be upgraded to non-canonical SuperToken logic.

### BridgedSuperToken

Gives mint and burn permisson to a single account `BRIDGE_ADDR`.
Hands upgradability permission to a hardcoded account `UPGRADE_ADMIN`.

Fork testing using FRACTION on Optimism:

```
ADMIN_ADDR=0x388e96dfe68b30892af93f30f5035602d8d51487 yarn test:fork:fraction-on-op
```

Deploy (to Optimism Goerli, using the deployer account as UPGRADE_ADMIN):

```
npx truffle exec --network opgoerli scripts/deploy-bridgedsupertoken.js
```
79 changes: 79 additions & 0 deletions contracts/alternative-logic/BridgedSuperToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.0;

import { SuperToken } from "@superfluid-finance/ethereum-contracts/contracts/superfluid/SuperToken.sol";
import { SuperfluidToken } from "@superfluid-finance/ethereum-contracts/contracts/superfluid/SuperfluidToken.sol";
import { ISuperfluid } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
import { IConstantOutflowNFT } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/IConstantOutflowNFT.sol";
import { IConstantInflowNFT } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/IConstantInflowNFT.sol";

interface IMintBurn {
function mint(address to, uint256 amount) external;
function burn(address from, uint256 amount) external;
}

/**
* This is a variant of the SuperToken logic with the following changes:
* - simple mint/burn interface which can be called only by a hardcoded BRIDGE_ADDR
* - update admin changes from host to a hardcoded UPGRADE_ADMIN
*
* This allows Pure SuperToken representations (deployments on other than the home chain) like MIVA and FRACTION
* to be used with the Connext bridge.
*/
contract BridgedSuperToken is SuperToken, IMintBurn {
// the account with upgrade permission. In order to change, upgrade to a logic with different value.
address public immutable UPGRADE_ADMIN;

// the account with mint/burn permission. In order to change, upgrade to a logic with different value.
address public immutable BRIDGE_ADDR;

error NO_PERMISSION();

constructor(
ISuperfluid host,
IConstantOutflowNFT constantOutflowNFT,
IConstantInflowNFT constantInflowNFT,
address upgradeAdmin,
address bridgeAddr
)
SuperToken(host, constantOutflowNFT, constantInflowNFT)
{
UPGRADE_ADMIN = upgradeAdmin;
BRIDGE_ADDR = bridgeAddr;
}

// TODO: shall we use mint/burn with or without hooks? If without, add events

function burn(address from, uint256 amount) external override {
if (msg.sender != BRIDGE_ADDR) revert NO_PERMISSION();
this.selfBurn(from, amount, new bytes(0));
//SuperfluidToken._mint(from, amount);
}

function mint(address to, uint256 amount) external {
if (msg.sender != BRIDGE_ADDR) revert NO_PERMISSION();
this.selfMint(to, amount, new bytes(0));
//SuperfluidToken._mint(to, amount);
}

// Make the token self-sovereign

/// IMPORTANT: this function needs to stay in sync with the canonical version of SuperToken
function updateCode(address newAddress) external override {
if (msg.sender != UPGRADE_ADMIN) revert NO_PERMISSION();
// implementation in UUPSProxiable
_updateCodeAddress(newAddress);

// @note This is another check to ensure that when updating to a new SuperToken logic contract
// that we have passed the correct NFT proxy contracts in the construction of the new SuperToken
// logic contract
if (
CONSTANT_OUTFLOW_NFT !=
SuperToken(newAddress).CONSTANT_OUTFLOW_NFT() ||
CONSTANT_INFLOW_NFT !=
SuperToken(newAddress).CONSTANT_INFLOW_NFT()
) {
revert SUPER_TOKEN_NFT_PROXY_ADDRESS_CHANGED();
}
}
}
6 changes: 6 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[profile.default]
# seems to have no effect (?)
src = 'contracts'
test = 'test/foundry/'
solc_version = "0.8.19"
out = 'build/foundry'
1 change: 1 addition & 0 deletions lib/forge-std
Submodule forge-std added at 74cfb7
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
"scripts": {
"build": "truffle compile --all",
"test": "truffle test",
"test:fork": "scripts/run-test-foundry.sh",
"test:fork:fraction-on-op": "TOKEN=0xbd80cfa9d93a87d1bb895f810ea348e496611cd4 scripts/run-fork-test.sh optimism-mainnet BridgedSuperTokenForkTest",
"prepare": "husky install"
},
"dependencies": {
"@openzeppelin/contracts": "^4.7.3",
"@superfluid-finance/ethereum-contracts": "^1.4.3",
"truffle-plugin-verify": "^0.6.0"
"@superfluid-finance/ethereum-contracts": "^1.7.1",
"@superfluid-finance/metadata": "^1.1.10",
"truffle-plugin-verify": "0.6.1"
},
"devDependencies": {
"@decentral.ee/web3-helpers": "^0.5.3",
Expand Down
95 changes: 95 additions & 0 deletions scripts/deploy-bridgedsupertoken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
const sfMeta = require("@superfluid-finance/metadata")

const BridgedSuperToken = artifacts.require("BridgedSuperToken")

const ISuperTokenFactoryArtifact = require("@superfluid-finance/ethereum-contracts/artifacts/contracts/interfaces/superfluid/ISuperTokenFactory.sol/ISuperTokenFactory")
const SuperTokenArtifact = require("@superfluid-finance/ethereum-contracts/artifacts/contracts/superfluid/SuperToken.sol/SuperToken")

// see https://docs.connext.network/resources/deployments
const CONNEXT_ADDRS = {
// testnets
5: "0xFCa08024A6D4bCc87275b1E4A1E22B71fAD7f649", // goerli
80001: "0x2334937846Ab2A3FCE747b32587e1A1A2f6EEC5a", // mumbai
420: "0x5Ea1bb242326044699C3d81341c5f535d5Af1504", // OP goerli
421613: "0x2075c9E31f973bb53CAE5BAC36a8eeB4B082ADC2", // Arb goerli
1442: "0x20b4789065DE09c71848b9A4FcAABB2c10006FA2", // zkEVM

// mainnets
1: "0x8898B472C54c31894e3B9bb83cEA802a5d0e63C6",
10: "0x8f7492DE823025b4CfaAB1D34c58963F2af5DEDA", // OP
137: "0x11984dc4465481512eb5b777E44061C158CF2259", // Matic
42161: "0xEE9deC2712cCE65174B561151701Bf54b99C24C8", // Arbitrum
56: "0xCd401c10afa37d641d2F594852DA94C700e4F2CE", // BSC
100: "0x5bB83e95f63217CDa6aE3D181BA580Ef377D2109" // Gnosis
}

/*
* Truffle script for deploying BridgedSuperToken
* optional env vars:
* - UPGRADE_ADMIN (default: deployer)
* - BRIDGE_ADDR (default: CONNEXT_ADDRS[chainId])
* - HOST (default: taken from metadata)
* - FACTORY (default: taken from metadata)
*/
module.exports = async function (callback) {
try {
const deployer = (await web3.eth.getAccounts())[0]
console.log("deployer: ", deployer)

const chainId = await web3.eth.getChainId()

const upgradeAdmin = process.env.UPGRADE_ADMIN || deployer
const bridgeAddr = process.env.BRIDGE_ADDR || CONNEXT_ADDRS[chainId]

if (bridgeAddr === undefined) {
throw new Error("no bridge address defined for chainId " + chainId)
}

console.log("chainId: ", chainId)

const network = sfMeta.getNetworkByChainId(chainId)
console.log("network: ", network.name)

const hostAddr = process.env.HOST || network.contractsV1.host
const factoryAddr =
process.env.FACTORY || network.contractsV1.superTokenFactory

const factory = new web3.eth.Contract(
ISuperTokenFactoryArtifact.abi,
factoryAddr
)

const curLogicAddr = await factory.methods.getSuperTokenLogic().call()
console.log("current logic: ", curLogicAddr)

// Get addresses of COF and CIF NFTs - note that this is NOT the same as the addresses in the factory contract
// This point to the proxies while those in the factory point to the logic contracts
const currentSTLogic = new web3.eth.Contract(
SuperTokenArtifact.abi,
curLogicAddr
)
const curCofNFTAddr = await currentSTLogic.methods
.CONSTANT_OUTFLOW_NFT()
.call()
const curCifNFTAddr = await currentSTLogic.methods
.CONSTANT_INFLOW_NFT()
.call()

console.log("COF NFT: ", curCofNFTAddr)
console.log("CIF NFT: ", curCifNFTAddr)

const newSTLogic = await BridgedSuperToken.new(
hostAddr,
curCofNFTAddr,
curCifNFTAddr,
upgradeAdmin,
bridgeAddr
)

console.log("Deployed BridgedSuperToken:", newSTLogic.address)

callback()
} catch (error) {
callback(error)
}
}
8 changes: 6 additions & 2 deletions scripts/deploy.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { web3tx } = require("@decentral.ee/web3-helpers")
const { setWeb3Provider } = require("@decentral.ee/web3-helpers/src/config")
const { factory: factoryAddrs } = require("./utils/constants")
const sfMeta = require("@superfluid-finance/metadata")

/*
* Truffle script for deploying a custom Super Token
Expand Down Expand Up @@ -45,7 +45,11 @@ module.exports = async function (callback) {
setWeb3Provider(web3.currentProvider)

const chainId = await web3.eth.net.getId()
const factoryAddr = process.env.FACTORY || factoryAddrs[chainId]

const network = sfMeta.getNetworkByChainId(chainId)

const factoryAddr =
process.env.FACTORY || network.contractsV1.superTokenFactory
if (factoryAddr === undefined) {
throw "ERR: No SuperTokenFactory address provided of found for the connected chain"
}
Expand Down
31 changes: 31 additions & 0 deletions scripts/run-fork-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash

# usage: run-fork-test.sh <network> <testContract> [<extraArg> ...]

set -eu

network=$1
testContract=$2
shift 2
extraArgs=$@

metadata=$(curl -s "https://raw.githubusercontent.com/superfluid-finance/protocol-monorepo/dev/packages/metadata/networks.json")

# takes the network name as argument
function test_network() {
rpc=${RPC:-"https://${network}.rpc.x.superfluid.dev"}

echo "=============== Testing $network... ==================="

# get current metadata

host=$(echo "$metadata" | jq -r '.[] | select(.name == "'$network'").contractsV1.host')

# Print the host address
echo "Host: $host"

set -x
RPC=$rpc HOST_ADDR=$host forge test --match-contract $testContract $extraArgs
}

test_network
Loading
Loading