Skip to content

Commit

Permalink
Governance stuff, cleanup, etc.
Browse files Browse the repository at this point in the history
  • Loading branch information
paulgoleary committed Oct 22, 2023
1 parent e8b46c1 commit 99804db
Show file tree
Hide file tree
Showing 10 changed files with 309 additions and 104 deletions.
3 changes: 3 additions & 0 deletions backend/chain/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import "github.com/umbracle/ethgo"
var MockMumbaiAddr = ethgo.HexToAddress("0x80FD6A0a454045d3E24B5BAa19b9066ae01a0b09")
var LuvMumbaiAddr = ethgo.HexToAddress("0x39C716D8c6E4D3B45Bc9e60f5C12378433668588")

var LuvVotesMumbaiAddr = ethgo.HexToAddress("0xAa885a92C8F0C625886d3e97D58cA07bC6Af46D8")
var LuvGovMumbaiAddr = ethgo.HexToAddress("0xeEDbe595DDCFB5AfDbA7E16B3a36B885CbA81A4A")

// Polygon Mainnet

var USDCPolygonMainnet = ethgo.HexToAddress("0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174")
Expand Down
7 changes: 7 additions & 0 deletions backend/chain/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ func (e *EcdsaKey) Sign(hash []byte) ([]byte, error) {
return crypto.Sign(e.SK, hash)
}

func (e *EcdsaKey) SKHex() string {
b := e.SK.D.Bytes()
bb := make([]byte, 32-len(b))
bb = append(bb, b...)
return hex.EncodeToString(bb)
}

var _ ethgo.Key = &EcdsaKey{}

func LoadContract(ec *jsonrpc.Client, name string, withKey ethgo.Key, addr ethgo.Address) (loaded *contract.Contract, err error) {
Expand Down
53 changes: 53 additions & 0 deletions backend/chain/governor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package chain

import (
"crypto/ecdsa"
"github.com/paulgoleary/local-luv-proto/crypto"
"github.com/umbracle/ethgo"
"github.com/umbracle/ethgo/contract"
"github.com/umbracle/ethgo/jsonrpc"
)

type govHelper struct {
gov *contract.Contract
votes *contract.Contract

k *EcdsaKey
}

func makeGovHelper(rpcUrl, skHex string, govAddr, votesAddr ethgo.Address) (gh *govHelper, err error) {

var ec *jsonrpc.Client
if ec, err = jsonrpc.NewClient(rpcUrl); err != nil {
return
}

var sk *ecdsa.PrivateKey
if sk, err = crypto.SKFromHex(skHex); err != nil {
return
}

gh = &govHelper{k: &EcdsaKey{SK: sk}}

if gh.gov, err = LoadContract(ec, "SFLUVGovernor.sol/SFLUVGovernorV0", gh.k, govAddr); err != nil {
return
}

if gh.votes, err = LoadContract(ec, "SFLUVVotes.sol/SFLUVVotesV1", gh.k, votesAddr); err != nil {
return
}

return
}

func (gh *govHelper) mintVote(toAddr ethgo.Address) (err error) {
if err = checkMinterRole(gh.votes, gh.k.Address()); err != nil {
return
}

if err = TxnDoWait(gh.votes.Txn("mint", toAddr)); err != nil {
return
}

return
}
25 changes: 25 additions & 0 deletions backend/chain/governor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package chain

import (
"fmt"
"github.com/paulgoleary/local-luv-proto/crypto"
"github.com/stretchr/testify/require"
"os"
"testing"
)

func TestDAOMumbai(t *testing.T) {

gh, err := makeGovHelper(os.Getenv("CHAIN_URL"), os.Getenv("CHAIN_SK"), LuvGovMumbaiAddr, LuvVotesMumbaiAddr)
require.NoError(t, err)

for i := 0; i < 3; i++ {
sk, _ := crypto.RandSK()
ec := EcdsaKey{SK: sk}

err = gh.mintVote(ec.Address())
require.NoError(t, err)

fmt.Printf("address '%v', sk '%v'\n", ec.Address().String(), ec.SKHex())
}
}
121 changes: 121 additions & 0 deletions backend/chain/mint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package chain

import (
"crypto/ecdsa"
"fmt"
"github.com/paulgoleary/local-luv-proto/crypto"
"github.com/umbracle/ethgo"
"github.com/umbracle/ethgo/contract"
"github.com/umbracle/ethgo/jsonrpc"
"math/big"
)

type mintHelper struct {
baseCoin *contract.Contract
luvCoin *contract.Contract

luvAddr ethgo.Address
k *EcdsaKey
}

func makeMintHelper(rpcUrl, skHex string, luvAddr, baseAddr ethgo.Address) (mh *mintHelper, err error) {

var ec *jsonrpc.Client
if ec, err = jsonrpc.NewClient(rpcUrl); err != nil {
return
}

var sk *ecdsa.PrivateKey
if sk, err = crypto.SKFromHex(skHex); err != nil {
return
}

mh = &mintHelper{k: &EcdsaKey{SK: sk}, luvAddr: luvAddr}

if mh.luvCoin, err = LoadContract(ec, "SFLUVv1.sol/SFLUVv1", mh.k, luvAddr); err != nil {
return
}

// MockCoin is ERC20 but also has the 'mint' method in case we need to mint base coins
if mh.baseCoin, err = LoadContract(ec, "MockCoin.sol/MockCoin", mh.k, baseAddr); err != nil {
return
}

return
}

// check that the provided key has the MINTER role and - if not - attempt to add it
// note that the configured key has to be an ADMIN of the MINTER role
func checkMinterRole(c *contract.Contract, toAddr ethgo.Address) error {
resp, err := c.Call("MINTER_ROLE", ethgo.Latest)
if err != nil {
return err
}
minterRole, ok := resp["0"].([32]byte)
if !ok {
return fmt.Errorf("should not happen - target contract does not support MINTER_ROLE")
}

resp, err = c.Call("hasRole", ethgo.Latest, minterRole, toAddr)
if err != nil {
return err
}
hasRole, ok := resp["0"].(bool)

if !hasRole {
if err = TxnDoWait(c.Txn("grantRole", minterRole, toAddr)); err != nil {
return err
}
}

return nil
}

// this only works if we're allowed to mint against the base coin contract - e.g. it's the mock coin
func (mh *mintHelper) mintBase(toAddr ethgo.Address, amt *big.Int) (err error) {

var tx contract.Txn
if tx, err = mh.baseCoin.Txn("mint", toAddr, amt); err != nil {
return
}
tx.WithOpts(&contract.TxnOpts{GasLimit: 100_000})

if err = TxnDoWait(tx, nil); err != nil {
return
}

return
}

// assumes provided key owns sufficient amount in base coin
func (mh *mintHelper) mintLuv(toAddr ethgo.Address, amt *big.Int) (err error) {

if err = checkMinterRole(mh.luvCoin, mh.k.Address()); err != nil {
return
}

// function allowance(address owner, address spender) external view returns (uint256);
var resp map[string]any
if resp, err = mh.baseCoin.Call("allowance", ethgo.Latest, mh.k.Address(), mh.luvAddr); err != nil {
return
}
allowance, ok := resp["0"].(*big.Int)
if !ok {
err = fmt.Errorf("should not happen - 'allowance' method did not return proper result")
return
}
if allowance.Cmp(amt) < 0 {
// current allowance is not sufficient
allowance.Sub(amt, allowance)
// function approve(address spender, uint256 amount) public virtual override returns (bool)
if err = TxnDoWait(mh.baseCoin.Txn("approve", mh.luvAddr, allowance)); err != nil {
return
}
}

if err = TxnDoWait(mh.luvCoin.Txn("depositFor", toAddr, allowance)); err != nil {
return
}

return
}
93 changes: 11 additions & 82 deletions backend/chain/mint_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package chain

import (
"github.com/paulgoleary/local-luv-proto/crypto"
"github.com/stretchr/testify/require"
"github.com/umbracle/ethgo"
"github.com/umbracle/ethgo/contract"
"github.com/umbracle/ethgo/jsonrpc"
"math/big"
"os"
"testing"
Expand All @@ -14,98 +11,30 @@ import (
var billyzWallet = ethgo.HexToAddress("0xEF17dc60E4D58Fd24D3b6FCDF07e3C5029018863")

func noTestSFLUVMint(t *testing.T) {
ec, err := jsonrpc.NewClient(os.Getenv("CHAIN_URL"))
require.NoError(t, err)

sk, err := crypto.SKFromHex(os.Getenv("CHAIN_SK"))
mh, err := makeMintHelper(os.Getenv("CHAIN_URL"), os.Getenv("CHAIN_SK"), LuvMumbaiAddr, MockMumbaiAddr)
require.NoError(t, err)
k := &EcdsaKey{SK: sk}

if false {
ep, err := LoadContract(ec, "ERC20.sol/ERC20", k, USDCPolygonMainnet)
require.NoError(t, err)

// function approve(address spender, uint256 amount) public virtual override returns (bool)
err = TxnDoWait(ep.Txn("approve", SFLUVPolygonMainnetV1_1, big.NewInt(1*1_000_000)))
require.NoError(t, err)

// function allowance(address owner, address spender) external view returns (uint256);
resp, err := ep.Call("allowance", ethgo.Latest, k.Address(), SFLUVPolygonMainnetV1_1)
require.NoError(t, err)
_ = resp
}

ep, err := LoadContract(ec, "SFLUVv1.sol/SFLUVv1", k, SFLUVPolygonMainnetV1_1)
err = mh.mintLuv(LuvGovMumbaiAddr, ethgo.Ether(1000))
require.NoError(t, err)

if true {
resp, err := ep.Call("MINTER_ROLE", ethgo.Latest)
require.NoError(t, err)
minterRole, ok := resp["0"].([32]byte)
require.True(t, ok)

resp, err = ep.Call("hasRole", ethgo.Latest, minterRole, k.Address())
require.NoError(t, err)
hasRole, ok := resp["0"].(bool)
require.True(t, ok)

if !hasRole {
// testLUVCoin.grantRole(testLUVCoin.MINTER_ROLE(), testLUVCoin.owner());
err = TxnDoWait(ep.Txn("grantRole", minterRole, k.Address()))
require.NoError(t, err)
}

// err = TxnDoWait(ep.Txn("depositFor", k.Address(), big.NewInt(100*1_000_000)))
err = TxnDoWait(ep.Txn("depositFor", billyzWallet, big.NewInt(1_000)))
require.NoError(t, err)

resp, err = ep.Call("balanceOf", ethgo.Latest, k.Address())
require.NoError(t, err)
_ = resp
}

if false {
// function withdrawTo(address account, uint256 amount) public virtual returns (bool) {
err = TxnDoWait(ep.Txn("withdrawTo", k.Address(), big.NewInt(100*1_000_000)))
require.NoError(t, err)

resp, err := ep.Call("balanceOf", ethgo.Latest, k.Address())
require.NoError(t, err)
_ = resp
}

resp, err := mh.luvCoin.Call("balanceOf", ethgo.Latest, LuvGovMumbaiAddr)
require.NoError(t, err)
checkAmt, ok := resp["0"].(*big.Int)
require.True(t, ok)
require.Equal(t, 0, checkAmt.Cmp(ethgo.Ether(1000)))
}

func noTestMockCoinMint(t *testing.T) {

ec, err := jsonrpc.NewClient(os.Getenv("CHAIN_URL"))
require.NoError(t, err)

sk, err := crypto.SKFromHex(os.Getenv("CHAIN_SK"))
require.NoError(t, err)
k := &EcdsaKey{SK: sk}

ep, err := LoadContract(ec, "MockCoin.sol/MockCoin", k, MockMumbaiAddr)
require.NoError(t, err)

walletAddr := ethgo.HexToAddress("0x054dF6203225bB58d9243eBf9DAd55608a436042")

//res, err := ep.Call("balanceOf", ethgo.Latest, walletAddr)
//require.NoError(t, err)
//checkBalance, ok := res["0"].(*big.Int)
//require.True(t, ok)
//require.Equal(t, 0, checkBalance.Cmp(big.NewInt(0)))

tx, err := ep.Txn("mint", walletAddr, ethgo.Ether(100))
mh, err := makeMintHelper(os.Getenv("CHAIN_URL"), os.Getenv("CHAIN_SK"), LuvMumbaiAddr, MockMumbaiAddr)
require.NoError(t, err)
tx.WithOpts(&contract.TxnOpts{GasLimit: 100_000})

err = TxnDoWait(tx, nil)
err = mh.mintBase(mh.k.Address(), ethgo.Ether(1000))
require.NoError(t, err)

res, err := ep.Call("balanceOf", ethgo.Latest, walletAddr)
res, err := mh.baseCoin.Call("balanceOf", ethgo.Latest, mh.k.Address())
require.NoError(t, err)
checkBalance, ok := res["0"].(*big.Int)
require.True(t, ok)
require.Equal(t, 0, checkBalance.Cmp(ethgo.Ether(200)))
require.Equal(t, 0, checkBalance.Cmp(ethgo.Ether(1000)))
}
19 changes: 19 additions & 0 deletions contracts/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,22 @@ forge verify-contract --verifier-url https://api.polygonscan.com/api/ 0x58a2993A

# works with config in foundry.toml?
forge verify-contract --chain polygon 0x58a2993A618Afee681DE23dECBCF535A58A080BA src/SFLUVv1.sol:SFLUVv1

# Deploy V0 Governor to Mumbai w/ 'Polygon Dev' account

forge create --rpc-url https://polygon-mumbai.g.alchemy.com/v2/j8dCQId_xGnyvlQUAinogNkE5gStdUKn \
--private-key f2e606e7bcf2e3a9c3919c88bdf720c4e831d6785668e3a1c05be92dc2005bd0 \
src/SFLUVVotes.sol:SFLUVVotesV1

Deployer: 0x32A629dE3fb4549EB2B204d37eb9C8CFb0b9AdCf
Deployed to: 0xAa885a92C8F0C625886d3e97D58cA07bC6Af46D8
Transaction hash: 0x7b463c531290c848c350fe0c4c27bf503f56b5d9adca6a5969a4e1c7b9a0f01d

forge create --rpc-url https://polygon-mumbai.g.alchemy.com/v2/<API key> \
--constructor-args 0xAa885a92C8F0C625886d3e97D58cA07bC6Af46D8 \
--private-key <your private key> \
src/SFLUVGovernor.sol:SFLUVGovernorV0

Deployer: 0x32A629dE3fb4549EB2B204d37eb9C8CFb0b9AdCf
Deployed to: 0xeEDbe595DDCFB5AfDbA7E16B3a36B885CbA81A4A
Transaction hash: 0x2b972179057b58be2ca9545ad3963742eac63668e1bc4cd122e470bccfa0c543
Loading

0 comments on commit 99804db

Please sign in to comment.