Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
Copyright 2021 Set Labs Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

SPDX-License-Identifier: Apache License, Version 2.0
*/

pragma solidity 0.6.10;
pragma experimental "ABIEncoderV2";


/**
* @title SnapshotGovernanceAdapter
* @author teddy
*
* Governance adapter for Snapshot governance that returns data for delegating
*/
contract SnapshotGovernanceAdapter {

/* ============ Constants ============ */

// Signature of delegate function
string public constant DELEGATE_SIGNATURE = "setDelegate(bytes32,address)";

// Signature of revoke function
string public constant REVOKE_SIGNATURE = "clearDelegate(bytes32)";

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

// Address of Delegate Registry
address public immutable delegateRegistry;

/* ============ Constructor ============ */

/**
* Set state variables
*
* @param _delegateRegistry Address of Delegate Registry
*/
constructor(address _delegateRegistry) public {
delegateRegistry = _delegateRegistry;
}

/* ============ External Getter Functions ============ */

/**
* Reverts as Snapshot currently does not have a vote mechanism in governance
*/
function getVoteCalldata(uint256 /* _proposalId */, bool /*_support */, bytes memory /* _data */) external view returns (address, uint256, bytes memory) {
revert("No vote available in Snapshot governance");
}

/**
* Generates the calldata to delegate all votes of msg.sender to delegatee
*
* @param _delegatee Address of the delegatee
*
* @return address Target contract address
* @return uint256 Total quantity of ETH (Set to 0)
* @return bytes Propose calldata
*/
function getDelegateCalldata(address _delegatee) external view returns (address, uint256, bytes memory) {
bytes memory callData = abi.encodeWithSignature(DELEGATE_SIGNATURE, bytes32(0), _delegatee);

return (delegateRegistry, 0, callData);
}

/**
* Reverts as Snapshot currently does not have a register mechanism in governance
*/
function getRegisterCalldata(address /* _setToken */) external view returns (address, uint256, bytes memory) {
revert("No register available in Snapshot governance");
}

/**
* Generates the calldata to revokes all delegation from msg.sender
*
* @return address Target contract address
* @return uint256 Total quantity of ETH (Set to 0)
* @return bytes Propose calldata
*/
function getRevokeCalldata() external view returns (address, uint256, bytes memory) {
bytes memory callData = abi.encodeWithSignature(REVOKE_SIGNATURE, bytes32(0));

return (delegateRegistry, 0, callData);
}

/**
* Reverts as Snapshot currently does not have a propose mechanism in governance
*/
function getProposeCalldata(bytes memory /* _proposalData */) external view returns (address, uint256, bytes memory) {
revert("No propose available in Snapshot governance");
}
}
56 changes: 56 additions & 0 deletions external/abi/snapshot/DelegateRegistry.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"contractName": "DelegateRegistry",
"abi": [
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "address", "name": "delegator", "type": "address" },
{ "indexed": true, "internalType": "bytes32", "name": "id", "type": "bytes32" },
{ "indexed": true, "internalType": "address", "name": "delegate", "type": "address" }
],
"name": "ClearDelegate",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "address", "name": "delegator", "type": "address" },
{ "indexed": true, "internalType": "bytes32", "name": "id", "type": "bytes32" },
{ "indexed": true, "internalType": "address", "name": "delegate", "type": "address" }
],
"name": "SetDelegate",
"type": "event"
},
{
"inputs": [{ "internalType": "bytes32", "name": "id", "type": "bytes32" }],
"name": "clearDelegate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "", "type": "address" },
{ "internalType": "bytes32", "name": "", "type": "bytes32" }
],
"name": "delegation",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "bytes32", "name": "id", "type": "bytes32" },
{ "internalType": "address", "name": "delegate", "type": "address" }
],
"name": "setDelegate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": "0x608060405234801561001057600080fd5b50610794806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806374c6c45414610046578063bd86e508146100be578063f0bedbe21461010c575b600080fd5b6100926004803603604081101561005c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061013a565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61010a600480360360408110156100d457600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061017c565b005b6101386004803603602081101561012257600080fd5b8101908080359060200190929190505050610538565b005b60006020528160005260406000206020528060005260406000206000915091509054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b3373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561021e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f43616e27742064656c656761746520746f2073656c660000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156102c1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f43616e27742064656c656761746520746f20307830000000000000000000000081525060200191505060405180910390fd5b60008060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156103ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061073e6021913960400191505060405180910390fd5b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600085815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146104d8578073ffffffffffffffffffffffffffffffffffffffff16833373ffffffffffffffffffffffffffffffffffffffff167f9c4f00c4291262731946e308dc2979a56bd22cce8f95906b975065e96cd5a06460405160405180910390a45b8173ffffffffffffffffffffffffffffffffffffffff16833373ffffffffffffffffffffffffffffffffffffffff167fa9a7fd460f56bddb880a465a9c3e9730389c70bc53108148f16d55a87a6c468e60405160405180910390a4505050565b60008060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561064f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f2064656c656761746520736574000000000000000000000000000000000081525060200191505060405180910390fd5b60008060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff16823373ffffffffffffffffffffffffffffffffffffffff167f9c4f00c4291262731946e308dc2979a56bd22cce8f95906b975065e96cd5a06460405160405180910390a4505056fe416c72656164792064656c65676174656420746f20746869732061646472657373a2646970667358221220b6cd5a8d04426e1189563fbec7dfec4ba70090dc70fe05097a137991fe1b396964736f6c63430007020033",
"deployedBytecode": "",
"linkReferences": {},
"deployedLinkReferences": {}
}
154 changes: 154 additions & 0 deletions test/protocol/integration/governance/snapshotGovernanceAdapter.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import "module-alias/register";
import { BigNumber } from "@ethersproject/bignumber";

import { Address, Bytes } from "@utils/types";
import { Account } from "@utils/test/types";
import { EMPTY_BYTES, ZERO, ZERO_BYTES } from "@utils/constants";
import { DelegateRegistry, SnapshotGovernanceAdapter } from "@utils/contracts";
import DeployHelper from "@utils/deploys";
import {
addSnapshotBeforeRestoreAfterEach,
getAccounts,
getWaffleExpect,
getRandomAddress,
} from "@utils/test/index";

const expect = getWaffleExpect();

describe("SnapshotGovernanceAdapter", () => {
let owner: Account;
let mockSetToken: Account;
let deployer: DeployHelper;
let delegateRegistry: DelegateRegistry;

let snapshotGovernanceAdapter: SnapshotGovernanceAdapter;

before(async () => {
[owner, mockSetToken] = await getAccounts();

deployer = new DeployHelper(owner.wallet);

delegateRegistry = await deployer.external.deployDelegateRegistry();

snapshotGovernanceAdapter = await deployer.adapters.deploySnapshotGovernanceAdapter(
delegateRegistry.address,
);
});

addSnapshotBeforeRestoreAfterEach();

describe("#constructor", async () => {
let delegateRegistryAddress: Address;

beforeEach(() => {
delegateRegistryAddress = delegateRegistry.address;
});

async function subject() {
return deployer.adapters.deploySnapshotGovernanceAdapter(delegateRegistryAddress);
}

it("should have the correct delegate registry address", async () => {
const deployedSnapshotGovernanceAdapter = await subject();

const actualDelegateRegistryAddress = await deployedSnapshotGovernanceAdapter.delegateRegistry();

expect(actualDelegateRegistryAddress).to.eq(delegateRegistryAddress);
});
});

describe("#getVoteCalldata", async () => {
let proposalId: BigNumber;
let support: boolean;
let data: Bytes;

beforeEach(() => {
proposalId = ZERO;
support = true;
data = EMPTY_BYTES;
});

async function subject() {
return snapshotGovernanceAdapter.getVoteCalldata(proposalId, support, data);
}

it("should revert", async () => {
await expect(subject()).to.be.revertedWith("No vote available in Snapshot governance");
});
});

describe("#getDelegateCalldata", async () => {
let subjectDelegatee: Address;

beforeEach(async () => {
subjectDelegatee = await getRandomAddress();
});

async function subject() {
return snapshotGovernanceAdapter.getDelegateCalldata(subjectDelegatee);
}

it("should return correct data to delegate all votes", async () => {
const [targetAddress, ethValue, callData] = await subject();

const expectedCallData = delegateRegistry.interface.encodeFunctionData("setDelegate", [
ZERO_BYTES,
subjectDelegatee,
]);

expect(targetAddress).to.eq(delegateRegistry.address);
expect(ethValue).to.eq(ZERO);
expect(callData).to.eq(expectedCallData);
});
});

describe("#getRegisterCalldata", async () => {
let subjectSetToken: Address;

beforeEach(() => {
subjectSetToken = mockSetToken.address;
});

async function subject() {
return snapshotGovernanceAdapter.getRegisterCalldata(subjectSetToken);
}

it("should revert", async () => {
await expect(subject()).to.be.revertedWith("No register available in Snapshot governance");
});
});

describe("#getRevokeCalldata", async () => {
async function subject() {
return snapshotGovernanceAdapter.getRevokeCalldata();
}

it("should return correct data to revoke delegations", async () => {
const [targetAddress, ethValue, callData] = await subject();

const expectedCallData = delegateRegistry.interface.encodeFunctionData("clearDelegate", [
ZERO_BYTES,
]);

expect(targetAddress).to.eq(delegateRegistry.address);
expect(ethValue).to.eq(ZERO);
expect(callData).to.eq(expectedCallData);
});
});

describe("#getProposeCalldata", async () => {
let subjectProposalData: Bytes;

beforeEach(() => {
subjectProposalData = EMPTY_BYTES;
});

async function subject() {
return snapshotGovernanceAdapter.getProposeCalldata(subjectProposalData);
}

it("should revert", async () => {
await expect(subject()).to.be.revertedWith("No propose available in Snapshot governance");
});
});
});
2 changes: 2 additions & 0 deletions utils/contracts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export { CustomSetValuerMock } from "../../typechain/CustomSetValuerMock";
export { DebtIssuanceMock } from "../../typechain/DebtIssuanceMock";
export { DebtIssuanceModule } from "../../typechain/DebtIssuanceModule";
export { DebtModuleMock } from "../../typechain/DebtModuleMock";
export { DelegateRegistry } from "../../typechain/DelegateRegistry";
export { ExplicitERC20Mock } from "../../typechain/ExplicitERC20Mock";
export { ForceFunderMock } from "../../typechain/ForceFunderMock";
export { GaugeControllerMock } from "../../typechain/GaugeControllerMock";
Expand Down Expand Up @@ -67,6 +68,7 @@ export { SetToken } from "../../typechain/SetToken";
export { SetTokenCreator } from "../../typechain/SetTokenCreator";
export { SetValuer } from "../../typechain/SetValuer";
export { SingleIndexModule } from "../../typechain/SingleIndexModule";
export { SnapshotGovernanceAdapter } from "../../typechain/SnapshotGovernanceAdapter";
export { StakingAdapterMock } from "../../typechain/StakingAdapterMock";
export { StakingModule } from "../../typechain/StakingModule";
export { StakingRewards } from "../../typechain/StakingRewards";
Expand Down
2 changes: 2 additions & 0 deletions utils/contracts/snapshot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// External Snapshot Contracts
export { DelegateRegistry } from "../../typechain/DelegateRegistry";
6 changes: 6 additions & 0 deletions utils/deploys/deployAdapters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
UniswapV2IndexExchangeAdapter,
UniswapV2TransferFeeExchangeAdapter,
ZeroExApiAdapter,
SnapshotGovernanceAdapter,
SynthetixExchangeAdapter,
CompoundBravoGovernanceAdapter,
CompClaimAdapter,
Expand All @@ -42,6 +43,7 @@ import { YearnWrapAdapter__factory } from "../../typechain/factories/YearnWrapAd
import { UniswapPairPriceAdapter__factory } from "../../typechain/factories/UniswapPairPriceAdapter__factory";
import { UniswapV2TransferFeeExchangeAdapter__factory } from "../../typechain/factories/UniswapV2TransferFeeExchangeAdapter__factory";
import { UniswapV2IndexExchangeAdapter__factory } from "../../typechain/factories/UniswapV2IndexExchangeAdapter__factory";
import { SnapshotGovernanceAdapter__factory } from "../../typechain/factories/SnapshotGovernanceAdapter__factory";
import { SynthetixExchangeAdapter__factory } from "../../typechain/factories/SynthetixExchangeAdapter__factory";
import { CompoundBravoGovernanceAdapter__factory } from "../../typechain/factories/CompoundBravoGovernanceAdapter__factory";
import { CompClaimAdapter__factory } from "../../typechain";
Expand Down Expand Up @@ -163,6 +165,10 @@ export default class DeployAdapters {
return await new ZeroExApiAdapter__factory(this._deployerSigner).deploy(zeroExAddress, wethAddress);
}

public async deploySnapshotGovernanceAdapter(delegateRegistry: Address): Promise<SnapshotGovernanceAdapter> {
return await new SnapshotGovernanceAdapter__factory(this._deployerSigner).deploy(delegateRegistry);
}

public async deploySynthetixExchangeAdapter(
synthetixExchangerAddress: Address,
): Promise<SynthetixExchangeAdapter> {
Expand Down
7 changes: 7 additions & 0 deletions utils/deploys/deployExternal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ import {
} from "../contracts/index";
import { TokenSwap__factory } from "../../typechain/factories/TokenSwap__factory";

import { DelegateRegistry } from "../contracts/snapshot";
import { DelegateRegistry__factory } from "../../typechain/factories/DelegateRegistry__factory";

import {
SwapRouter,
Expand Down Expand Up @@ -571,6 +573,11 @@ export default class DeployExternalContracts {
return await new TokenSwap__factory(this._deployerSigner).deploy(oldToken, newToken);
}

// Snapshot
public async deployDelegateRegistry(): Promise<DelegateRegistry> {
return await new DelegateRegistry__factory(this._deployerSigner).deploy();
}

// Uniswap V3
public async deployUniswapV3Factory(): Promise<UniswapV3Factory> {
return await new UniswapV3Factory__factory(this._deployerSigner).deploy();
Expand Down