-
Notifications
You must be signed in to change notification settings - Fork 349
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add ERC20 utils * Add finance module and vesting submodule * Add interface for Vesting Component * Implement Vesting Component * Implement VestingWallet preset * Run linter * Update VestingSchedule trait to expect contract state as input param * Add mocks for vesting tests * Add tests for Vesting component * Add tests for Vesting preset * Add in-code documentation * Add entry to changelog * Fix linter issues * Fix tests * Fix batch of review issues * Lint files * Move default TestData init to helper function * Fix review issues * Fix review issues * Add test_release_after_ownership_transferred test case * Change the order of the events * Fix minor review issues * Fix compilation issues * Add readme and update Scarb.toml * Add tests for failed transfer assertion * Fix readme, remove vesting keyword, add missing trait import * Fix typo * Make final review fixes
- Loading branch information
Showing
26 changed files
with
1,395 additions
and
2 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
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,11 @@ | ||
## Finance | ||
|
||
This crate includes primitives for financial systems. | ||
|
||
### Interfaces | ||
|
||
- `IVesting` | ||
|
||
### Components | ||
|
||
- `VestingComponent` |
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,39 @@ | ||
[package] | ||
name = "openzeppelin_finance" | ||
readme = "README.md" | ||
keywords = [ | ||
"openzeppelin", | ||
"starknet", | ||
"contracts", | ||
"finance", | ||
"vesting" | ||
] | ||
version.workspace = true | ||
edition.workspace = true | ||
cairo-version.workspace = true | ||
scarb-version.workspace = true | ||
authors.workspace = true | ||
description.workspace = true | ||
documentation.workspace = true | ||
repository.workspace = true | ||
license-file.workspace = true | ||
|
||
[tool] | ||
fmt.workspace = true | ||
|
||
[dependencies] | ||
starknet.workspace = true | ||
openzeppelin_access = { path = "../access" } | ||
openzeppelin_token = { path = "../token" } | ||
|
||
[dev-dependencies] | ||
snforge_std.workspace = true | ||
openzeppelin_testing = { path = "../testing" } | ||
openzeppelin_test_common = { path = "../test_common" } | ||
|
||
[lib] | ||
|
||
[[target.starknet-contract]] | ||
allowed-libfuncs-list.name = "experimental" | ||
sierra = true | ||
casm = false |
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,2 @@ | ||
mod tests; | ||
pub mod vesting; |
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,7 @@ | ||
#[cfg(test)] | ||
pub(crate) mod common; | ||
pub(crate) mod mocks; | ||
#[cfg(test)] | ||
mod test_vesting_linear; | ||
#[cfg(test)] | ||
mod test_vesting_steps; |
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,71 @@ | ||
use openzeppelin_finance::vesting::interface::IVestingDispatcher; | ||
use openzeppelin_testing as utils; | ||
use openzeppelin_testing::constants; | ||
use openzeppelin_token::erc20::interface::IERC20Dispatcher; | ||
use openzeppelin_utils::serde::SerializedAppend; | ||
use starknet::{ContractAddress, SyscallResultTrait}; | ||
|
||
#[derive(Copy, Drop)] | ||
pub(crate) enum VestingStrategy { | ||
Linear, | ||
Steps: u64 | ||
} | ||
|
||
#[derive(Copy, Drop)] | ||
pub(crate) struct TestData { | ||
pub strategy: VestingStrategy, | ||
pub total_allocation: u256, | ||
pub beneficiary: ContractAddress, | ||
pub start: u64, | ||
pub duration: u64, | ||
pub cliff_duration: u64 | ||
} | ||
|
||
fn deploy_vesting_mock(data: TestData) -> IVestingDispatcher { | ||
let contract_address = match data.strategy { | ||
VestingStrategy::Linear => { | ||
let mut calldata = array![]; | ||
calldata.append_serde(data.beneficiary); | ||
calldata.append_serde(data.start); | ||
calldata.append_serde(data.duration); | ||
calldata.append_serde(data.cliff_duration); | ||
utils::declare_and_deploy("LinearVestingMock", calldata) | ||
}, | ||
VestingStrategy::Steps(total_steps) => { | ||
let mut calldata = array![]; | ||
calldata.append_serde(total_steps); | ||
calldata.append_serde(data.beneficiary); | ||
calldata.append_serde(data.start); | ||
calldata.append_serde(data.duration); | ||
calldata.append_serde(data.cliff_duration); | ||
utils::declare_and_deploy("StepsVestingMock", calldata) | ||
} | ||
}; | ||
IVestingDispatcher { contract_address } | ||
} | ||
|
||
fn deploy_erc20_mock(recipient: ContractAddress, initial_supply: u256) -> IERC20Dispatcher { | ||
let mut calldata = array![]; | ||
calldata.append_serde(constants::NAME()); | ||
calldata.append_serde(constants::SYMBOL()); | ||
calldata.append_serde(initial_supply); | ||
calldata.append_serde(recipient); | ||
|
||
let contract_address = utils::declare_and_deploy("ERC20Mock", calldata); | ||
IERC20Dispatcher { contract_address } | ||
} | ||
|
||
pub(crate) fn setup(data: TestData) -> (IVestingDispatcher, ContractAddress) { | ||
let vesting = deploy_vesting_mock(data); | ||
let token = deploy_erc20_mock(vesting.contract_address, data.total_allocation); | ||
(vesting, token.contract_address) | ||
} | ||
|
||
pub(crate) fn set_transfer_to_fail(token: ContractAddress, should_fail: bool) { | ||
let mut calldata = array![]; | ||
calldata.append_serde(true); | ||
starknet::syscalls::call_contract_syscall( | ||
token, selector!("set_transfer_should_fail"), calldata.span() | ||
) | ||
.unwrap_syscall(); | ||
} |
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,2 @@ | ||
pub(crate) mod erc20_mocks; | ||
pub(crate) mod vesting_mocks; |
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,91 @@ | ||
#[starknet::contract] | ||
pub(crate) mod ERC20Mock { | ||
use openzeppelin_token::erc20::interface::IERC20; | ||
use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; | ||
use starknet::ContractAddress; | ||
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; | ||
|
||
component!(path: ERC20Component, storage: erc20, event: ERC20Event); | ||
|
||
#[abi(embed_v0)] | ||
impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl<ContractState>; | ||
|
||
impl InternalImpl = ERC20Component::InternalImpl<ContractState>; | ||
|
||
#[storage] | ||
struct Storage { | ||
transfer_should_fail: bool, | ||
#[substorage(v0)] | ||
erc20: ERC20Component::Storage | ||
} | ||
|
||
#[event] | ||
#[derive(Drop, starknet::Event)] | ||
enum Event { | ||
#[flat] | ||
ERC20Event: ERC20Component::Event | ||
} | ||
|
||
#[constructor] | ||
fn constructor( | ||
ref self: ContractState, | ||
name: ByteArray, | ||
symbol: ByteArray, | ||
initial_supply: u256, | ||
recipient: ContractAddress | ||
) { | ||
self.erc20.initializer(name, symbol); | ||
self.erc20.mint(recipient, initial_supply); | ||
} | ||
|
||
#[abi(embed_v0)] | ||
impl ERC20Impl of IERC20<ContractState> { | ||
fn total_supply(self: @ContractState) -> u256 { | ||
self.erc20.total_supply() | ||
} | ||
|
||
fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { | ||
self.erc20.balance_of(account) | ||
} | ||
|
||
fn allowance( | ||
self: @ContractState, owner: ContractAddress, spender: ContractAddress | ||
) -> u256 { | ||
self.erc20.allowance(owner, spender) | ||
} | ||
|
||
fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool { | ||
if self.transfer_should_fail.read() { | ||
false | ||
} else { | ||
self.erc20.transfer(recipient, amount) | ||
} | ||
} | ||
|
||
fn transfer_from( | ||
ref self: ContractState, | ||
sender: ContractAddress, | ||
recipient: ContractAddress, | ||
amount: u256 | ||
) -> bool { | ||
if self.transfer_should_fail.read() { | ||
false | ||
} else { | ||
self.erc20.transfer_from(sender, recipient, amount) | ||
} | ||
} | ||
|
||
fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) -> bool { | ||
self.erc20.approve(spender, amount) | ||
} | ||
} | ||
|
||
#[generate_trait] | ||
#[abi(per_item)] | ||
impl ExternalImpl of ExternalTrait { | ||
#[external(v0)] | ||
fn set_transfer_should_fail(ref self: ContractState, should_fail: bool) { | ||
self.transfer_should_fail.write(should_fail); | ||
} | ||
} | ||
} |
Oops, something went wrong.