-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
create a contract for deploying the set of contracts all together (#18)
* create a contract for deploying the set of contracts all together * optionally deploy an airdrop contract * finish the deployment * more tests * add some methods to the airdrop and test some attributes of the airdrop in the factory * transfer tokens to the airdrop
- Loading branch information
1 parent
9732cba
commit b09f6d1
Showing
7 changed files
with
354 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
use starknet::{ContractAddress}; | ||
use governance::governor::{Config as GovernorConfig}; | ||
use governance::governor::{IGovernorDispatcher}; | ||
use governance::governance_token::{IGovernanceTokenDispatcher}; | ||
use governance::airdrop::{IAirdropDispatcher}; | ||
use governance::timelock::{ITimelockDispatcher}; | ||
|
||
#[derive(Copy, Drop, Serde)] | ||
struct AirdropConfig { | ||
root: felt252, | ||
total: u128, | ||
} | ||
|
||
#[derive(Copy, Drop, Serde)] | ||
struct TimelockConfig { | ||
delay: u64, | ||
window: u64, | ||
} | ||
|
||
#[derive(Copy, Drop, Serde)] | ||
struct DeploymentParameters { | ||
name: felt252, | ||
symbol: felt252, | ||
total_supply: u128, | ||
governor_config: GovernorConfig, | ||
timelock_config: TimelockConfig, | ||
airdrop_config: Option<AirdropConfig>, | ||
} | ||
|
||
#[derive(Copy, Drop, Serde)] | ||
struct DeploymentResult { | ||
token: IGovernanceTokenDispatcher, | ||
governor: IGovernorDispatcher, | ||
timelock: ITimelockDispatcher, | ||
airdrop: Option<IAirdropDispatcher>, | ||
} | ||
|
||
// This contract makes it easy to deploy a set of governance contracts from a block explorer just by specifying parameters | ||
#[starknet::interface] | ||
trait IFactory<TContractState> { | ||
fn deploy(self: @TContractState, params: DeploymentParameters) -> DeploymentResult; | ||
} | ||
|
||
#[starknet::contract] | ||
mod Factory { | ||
use super::{ | ||
IFactory, DeploymentParameters, DeploymentResult, ContractAddress, | ||
IGovernanceTokenDispatcher, IAirdropDispatcher, IGovernorDispatcher, ITimelockDispatcher | ||
}; | ||
use core::result::{ResultTrait}; | ||
use starknet::{ClassHash, deploy_syscall, get_caller_address}; | ||
use governance::interfaces::erc20::{IERC20Dispatcher, IERC20DispatcherTrait}; | ||
|
||
#[storage] | ||
struct Storage { | ||
governance_token: ClassHash, | ||
airdrop: ClassHash, | ||
governor: ClassHash, | ||
timelock: ClassHash, | ||
} | ||
|
||
#[constructor] | ||
fn constructor( | ||
ref self: ContractState, | ||
governance_token: ClassHash, | ||
airdrop: ClassHash, | ||
governor: ClassHash, | ||
timelock: ClassHash | ||
) { | ||
self.governance_token.write(governance_token); | ||
self.airdrop.write(airdrop); | ||
self.governor.write(governor); | ||
self.timelock.write(timelock); | ||
} | ||
|
||
#[external(v0)] | ||
impl FactoryImpl of IFactory<ContractState> { | ||
fn deploy(self: @ContractState, params: DeploymentParameters) -> DeploymentResult { | ||
let mut token_constructor_args: Array<felt252> = ArrayTrait::new(); | ||
Serde::serialize( | ||
@(params.name, params.symbol, params.total_supply), ref token_constructor_args | ||
); | ||
|
||
let (token_address, _) = deploy_syscall( | ||
class_hash: self.governance_token.read(), | ||
contract_address_salt: 0, | ||
calldata: token_constructor_args.span(), | ||
deploy_from_zero: false, | ||
) | ||
.unwrap(); | ||
|
||
let erc20 = IERC20Dispatcher { contract_address: token_address }; | ||
|
||
let mut governor_constructor_args: Array<felt252> = ArrayTrait::new(); | ||
Serde::serialize( | ||
@(token_address, params.governor_config), ref governor_constructor_args | ||
); | ||
|
||
let (governor_address, _) = deploy_syscall( | ||
class_hash: self.governor.read(), | ||
contract_address_salt: 0, | ||
calldata: governor_constructor_args.span(), | ||
deploy_from_zero: false, | ||
) | ||
.unwrap(); | ||
|
||
let (airdrop, remaining_amount) = match params.airdrop_config { | ||
Option::Some(config) => { | ||
let mut airdrop_constructor_args: Array<felt252> = ArrayTrait::new(); | ||
Serde::serialize(@(token_address, config.root), ref airdrop_constructor_args); | ||
|
||
let (airdrop_address, _) = deploy_syscall( | ||
class_hash: self.airdrop.read(), | ||
contract_address_salt: 0, | ||
calldata: airdrop_constructor_args.span(), | ||
deploy_from_zero: false, | ||
) | ||
.unwrap(); | ||
|
||
assert(config.total <= params.total_supply, 'AIRDROP_GT_SUPPLY'); | ||
|
||
erc20.transfer(airdrop_address, config.total.into()); | ||
|
||
( | ||
Option::Some(IAirdropDispatcher { contract_address: airdrop_address }), | ||
params.total_supply - config.total | ||
) | ||
}, | ||
Option::None => { (Option::None, params.total_supply) } | ||
}; | ||
|
||
erc20.transfer(get_caller_address(), remaining_amount.into()); | ||
|
||
let mut timelock_constructor_args: Array<felt252> = ArrayTrait::new(); | ||
Serde::serialize( | ||
@(governor_address, params.timelock_config.delay, params.timelock_config.window), | ||
ref timelock_constructor_args | ||
); | ||
|
||
let (timelock_address, _) = deploy_syscall( | ||
class_hash: self.timelock.read(), | ||
contract_address_salt: 0, | ||
calldata: timelock_constructor_args.span(), | ||
deploy_from_zero: false, | ||
) | ||
.unwrap(); | ||
|
||
DeploymentResult { | ||
token: IGovernanceTokenDispatcher { contract_address: token_address }, | ||
airdrop, | ||
governor: IGovernorDispatcher { contract_address: governor_address }, | ||
timelock: ITimelockDispatcher { contract_address: timelock_address } | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
use array::{ArrayTrait}; | ||
use debug::PrintTrait; | ||
use governance::interfaces::erc20::{IERC20Dispatcher, IERC20DispatcherTrait}; | ||
use governance::governor::{Config as GovernorConfig}; | ||
use governance::factory::{ | ||
IFactoryDispatcher, IFactoryDispatcherTrait, Factory, DeploymentParameters, AirdropConfig, | ||
TimelockConfig, | ||
}; | ||
use governance::governance_token::{GovernanceToken}; | ||
use governance::governor::{Governor}; | ||
use governance::timelock::{Timelock}; | ||
use governance::airdrop::{Airdrop}; | ||
use starknet::{ | ||
get_contract_address, deploy_syscall, ClassHash, contract_address_const, ContractAddress, | ||
}; | ||
use starknet::class_hash::{Felt252TryIntoClassHash}; | ||
use starknet::testing::{set_contract_address, set_block_timestamp, pop_log}; | ||
use traits::{TryInto}; | ||
|
||
use governance::governor::{IGovernorDispatcherTrait}; | ||
use governance::governance_token::{IGovernanceTokenDispatcherTrait}; | ||
use governance::airdrop::{IAirdropDispatcherTrait}; | ||
use governance::timelock::{ITimelockDispatcherTrait}; | ||
|
||
use result::{Result, ResultTrait}; | ||
use option::{OptionTrait}; | ||
|
||
fn deploy() -> IFactoryDispatcher { | ||
let mut constructor_args: Array<felt252> = ArrayTrait::new(); | ||
Serde::serialize( | ||
@( | ||
GovernanceToken::TEST_CLASS_HASH, | ||
Airdrop::TEST_CLASS_HASH, | ||
Governor::TEST_CLASS_HASH, | ||
Timelock::TEST_CLASS_HASH | ||
), | ||
ref constructor_args | ||
); | ||
|
||
let (address, _) = deploy_syscall( | ||
class_hash: Factory::TEST_CLASS_HASH.try_into().unwrap(), | ||
contract_address_salt: 0, | ||
calldata: constructor_args.span(), | ||
deploy_from_zero: true | ||
) | ||
.expect('DEPLOY_FAILED'); | ||
return IFactoryDispatcher { contract_address: address }; | ||
} | ||
|
||
|
||
#[test] | ||
#[available_gas(30000000)] | ||
fn test_deploy() { | ||
let factory = deploy(); | ||
|
||
let result = factory | ||
.deploy( | ||
DeploymentParameters { | ||
name: 'token', | ||
symbol: 'tk', | ||
total_supply: 5678, | ||
airdrop_config: Option::Some(AirdropConfig { root: 'root', total: 1111 }), | ||
governor_config: GovernorConfig { | ||
voting_start_delay: 0, | ||
voting_period: 180, | ||
voting_weight_smoothing_duration: 30, | ||
quorum: 1000, | ||
proposal_creation_threshold: 100, | ||
}, | ||
timelock_config: TimelockConfig { delay: 320, window: 60, } | ||
} | ||
); | ||
|
||
let erc20 = IERC20Dispatcher { contract_address: result.token.contract_address }; | ||
|
||
assert(erc20.name() == 'token', 'name'); | ||
assert(erc20.symbol() == 'tk', 'symbol'); | ||
assert(erc20.decimals() == 18, 'decimals'); | ||
assert(erc20.totalSupply() == 5678, 'totalSupply'); | ||
assert(erc20.balance_of(get_contract_address()) == 5678 - 1111, 'deployer balance'); | ||
assert(erc20.balance_of(result.airdrop.unwrap().contract_address) == 1111, 'airdrop balance'); | ||
|
||
let drop = result.airdrop.unwrap(); | ||
assert(drop.get_root() == 'root', 'airdrop root'); | ||
assert(drop.get_token().contract_address == result.token.contract_address, 'airdrop token'); | ||
|
||
assert( | ||
result.governor.get_voting_token().contract_address == result.token.contract_address, | ||
'voting_token' | ||
); | ||
assert( | ||
result | ||
.governor | ||
.get_config() == GovernorConfig { | ||
voting_start_delay: 0, | ||
voting_period: 180, | ||
voting_weight_smoothing_duration: 30, | ||
quorum: 1000, | ||
proposal_creation_threshold: 100, | ||
}, | ||
'governor.config' | ||
); | ||
assert(result.timelock.get_configuration() == (320, 60), 'timelock config'); | ||
} |
Oops, something went wrong.