Skip to content

Commit

Permalink
Merge pull request #16 from khaeljy/khaeljy/issue15
Browse files Browse the repository at this point in the history
Add positions list
  • Loading branch information
khaeljy authored Jan 4, 2024
2 parents 2fe56e8 + 3ffcfcc commit 57962fd
Show file tree
Hide file tree
Showing 14 changed files with 436 additions and 253 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
- uses: actions/checkout@v3
- uses: software-mansion/setup-scarb@v1
with:
scarb-version: "0.7.0"
scarb-version: "2.4.1"
- run: scarb fmt --check
- run: scarb build
- run: scarb test
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
target
.env
accounts
accounts/*.json
14 changes: 14 additions & 0 deletions Scarb.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "openzeppelin"
version = "0.8.0"
source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.8.0#c23e8e96de60e6e3159b1ff8591a1187269c0eb7"

[[package]]
name = "swappy"
version = "0.0.1"
dependencies = [
"openzeppelin",
]
2 changes: 1 addition & 1 deletion Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ version = "0.0.1"

[dependencies]
starknet = ">=2.2.0"
openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.7.0-rc.0" }
openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.8.0" }

[[target.starknet-contract]]

Expand Down
55 changes: 55 additions & 0 deletions accounts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Create a new signer
```
starkli signer keystore new accounts/keystore.json
export STARKNET_KEYSTORE=accounts/keystore.json
```

# Create the deployer account
```
starkli account oz init accounts/deployer.json
starkli account deploy accounts/deployer.json
```

# Declare Swappy Account Contract
```
starkli declare target/dev/swappy_Account.contract_class.json --account accounts/deployer.json
# 0x03dd8bbd25d0807d830cc78f3aef5e759c186c8f91efe1fcf3531132fdcade4b
```

# Create a swappy account
```
starkli account oz init accounts/swappy.json
```
> update class_hash
```
starkli account deploy accounts/swappy.json
```

# Deploy Position Manager Contract
```
starkli declare target/dev/swappy_PositionManager.contract_class.json --account accounts/deployer.json
# 0x0303b3c6adfb5015a09bce62b23428bb3e3b4bca3726a27919ae78763b5e993a
starkli deploy 0x0303b3c6adfb5015a09bce62b23428bb3e3b4bca3726a27919ae78763b5e993a 0x0 --account accounts/deployer.json
# 0x0097ab8a6dc7760a687caaffa7101611b20babda533ce40b3cac94fb1926355e
```

# Get last position ID
```
starkli call 0x0097ab8a6dc7760a687caaffa7101611b20babda533ce40b3cac94fb1926355e get_last_id
```

# create position
```
starkli invoke 0x0097ab8a6dc7760a687caaffa7101611b20babda533ce40b3cac94fb1926355e create_position 0x1 0x2 u256:1 u256:1 --account accounts/swappy.json
```

# Pause position
```
starkli invoke 0x0097ab8a6dc7760a687caaffa7101611b20babda533ce40b3cac94fb1926355e pause_position [POSITION_ID] --account accounts/swappy.json
```

# Resume position
```
starkli invoke 0x0097ab8a6dc7760a687caaffa7101611b20babda533ce40b3cac94fb1926355e resume_position [POSITION_ID] --account accounts/swappy.json
```
264 changes: 35 additions & 229 deletions src/account/account.cairo
Original file line number Diff line number Diff line change
@@ -1,244 +1,50 @@
use array::ArrayTrait;
use array::SpanTrait;
use option::OptionTrait;
use serde::Serde;
use starknet::ContractAddress;
use starknet::account::Call;

const TRANSACTION_VERSION: felt252 = 1;

// 2**128 + TRANSACTION_VERSION
const QUERY_VERSION: felt252 = 340282366920938463463374607431768211457;

trait PublicKeyTrait<TState> {
fn set_public_key(ref self: TState, new_public_key: felt252);
fn get_public_key(self: @TState) -> felt252;
}

trait PublicKeyCamelTrait<TState> {
fn setPublicKey(ref self: TState, newPublicKey: felt252);
fn getPublicKey(self: @TState) -> felt252;
}

#[starknet::contract]
mod Account {
use array::ArrayTrait;
use array::SpanTrait;
use box::BoxTrait;
use ecdsa::check_ecdsa_signature;

use openzeppelin::account::interface;
use openzeppelin::introspection::interface::ISRC5;
use openzeppelin::introspection::interface::ISRC5Camel;
use openzeppelin::introspection::src5::SRC5;
use option::OptionTrait;
use starknet::get_caller_address;
use starknet::get_contract_address;
use starknet::get_tx_info;

use super::Call;
use super::QUERY_VERSION;
use super::TRANSACTION_VERSION;
use zeroable::Zeroable;

use swappy::account::error::AccountError;
use openzeppelin::account::AccountComponent;
use openzeppelin::introspection::src5::SRC5Component;

component!(path: AccountComponent, storage: account, event: AccountEvent);
component!(path: SRC5Component, storage: src5, event: SRC5Event);

// Account
#[abi(embed_v0)]
impl SRC6Impl = AccountComponent::SRC6Impl<ContractState>;
#[abi(embed_v0)]
impl SRC6CamelOnlyImpl = AccountComponent::SRC6CamelOnlyImpl<ContractState>;
#[abi(embed_v0)]
impl PublicKeyImpl = AccountComponent::PublicKeyImpl<ContractState>;
#[abi(embed_v0)]
impl PublicKeyCamelImpl = AccountComponent::PublicKeyCamelImpl<ContractState>;
#[abi(embed_v0)]
impl DeclarerImpl = AccountComponent::DeclarerImpl<ContractState>;
#[abi(embed_v0)]
impl DeployableImpl = AccountComponent::DeployableImpl<ContractState>;

impl AccountInternalImpl = AccountComponent::InternalImpl<ContractState>;

// SRC5
#[abi(embed_v0)]
impl SRC5Impl = SRC5Component::SRC5Impl<ContractState>;

#[storage]
struct Storage {
public_key: felt252
#[substorage(v0)]
account: AccountComponent::Storage,
#[substorage(v0)]
src5: SRC5Component::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
OwnerAdded: OwnerAdded,
OwnerRemoved: OwnerRemoved,
}

#[derive(Drop, starknet::Event)]
struct OwnerAdded {
new_owner_guid: felt252
}

#[derive(Drop, starknet::Event)]
struct OwnerRemoved {
removed_owner_guid: felt252
#[flat]
AccountEvent: AccountComponent::Event,
#[flat]
SRC5Event: SRC5Component::Event
}

#[constructor]
fn constructor(ref self: ContractState, _public_key: felt252) {
self.initializer(_public_key);
}

//
// External
//

#[external(v0)]
impl SRC6Impl of interface::ISRC6<ContractState> {
fn __execute__(self: @ContractState, mut calls: Array<Call>) -> Array<Span<felt252>> {
// Avoid calls from other contracts
let sender = get_caller_address();
assert(sender.is_zero(), AccountError::INVALID_CALLER);

// Check tx version
let tx_info = get_tx_info().unbox();
let version = tx_info.version;
if version != TRANSACTION_VERSION {
assert(version == QUERY_VERSION, AccountError::INVALID_TX_VERSION);
}

_execute_calls(calls)
}

fn __validate__(self: @ContractState, mut calls: Array<Call>) -> felt252 {
self.validate_transaction()
}

fn is_valid_signature(
self: @ContractState, hash: felt252, signature: Array<felt252>
) -> felt252 {
if self._is_valid_signature(hash, signature.span()) {
starknet::VALIDATED
} else {
0
}
}
}

#[external(v0)]
impl SRC6CamelOnlyImpl of interface::ISRC6CamelOnly<ContractState> {
fn isValidSignature(
self: @ContractState, hash: felt252, signature: Array<felt252>
) -> felt252 {
SRC6Impl::is_valid_signature(self, hash, signature)
}
}

#[external(v0)]
impl DeclarerImpl of interface::IDeclarer<ContractState> {
fn __validate_declare__(self: @ContractState, class_hash: felt252) -> felt252 {
self.validate_transaction()
}
}

#[external(v0)]
impl SRC5Impl of ISRC5<ContractState> {
fn supports_interface(self: @ContractState, interface_id: felt252) -> bool {
let unsafe_state = SRC5::unsafe_new_contract_state();
SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id)
}
}

#[external(v0)]
impl SRC5CamelImpl of ISRC5Camel<ContractState> {
fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool {
let unsafe_state = SRC5::unsafe_new_contract_state();
SRC5::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId)
}
}

#[external(v0)]
impl PublicKeyImpl of super::PublicKeyTrait<ContractState> {
fn get_public_key(self: @ContractState) -> felt252 {
self.public_key.read()
}

fn set_public_key(ref self: ContractState, new_public_key: felt252) {
assert_only_self();
self.emit(OwnerRemoved { removed_owner_guid: self.public_key.read() });
self._set_public_key(new_public_key);
}
}

#[external(v0)]
impl PublicKeyCamelImpl of super::PublicKeyCamelTrait<ContractState> {
fn getPublicKey(self: @ContractState) -> felt252 {
self.public_key.read()
}

fn setPublicKey(ref self: ContractState, newPublicKey: felt252) {
PublicKeyImpl::set_public_key(ref self, newPublicKey);
}
}

#[external(v0)]
fn __validate_deploy__(
self: @ContractState,
class_hash: felt252,
contract_address_salt: felt252,
_public_key: felt252
) -> felt252 {
self.validate_transaction()
}

//
// Internal
//

#[generate_trait]
impl InternalImpl of InternalTrait {
fn initializer(ref self: ContractState, _public_key: felt252) {
let mut unsafe_state = SRC5::unsafe_new_contract_state();
SRC5::InternalImpl::register_interface(ref unsafe_state, interface::ISRC6_ID);
self._set_public_key(_public_key);
}

fn validate_transaction(self: @ContractState) -> felt252 {
let tx_info = get_tx_info().unbox();
let tx_hash = tx_info.transaction_hash;
let signature = tx_info.signature;
assert(self._is_valid_signature(tx_hash, signature), AccountError::INVALID_SIGNATURE);
starknet::VALIDATED
}

fn _set_public_key(ref self: ContractState, new_public_key: felt252) {
self.public_key.write(new_public_key);
self.emit(OwnerAdded { new_owner_guid: new_public_key });
}

fn _is_valid_signature(
self: @ContractState, hash: felt252, signature: Span<felt252>
) -> bool {
let valid_length = signature.len() == 2_u32;

if valid_length {
check_ecdsa_signature(
hash, self.public_key.read(), *signature.at(0_u32), *signature.at(1_u32)
)
} else {
false
}
}
}

#[internal]
fn assert_only_self() {
let caller = get_caller_address();
let self = get_contract_address();
assert(self == caller, AccountError::UNAUTHORIZED);
}

#[internal]
fn _execute_calls(mut calls: Array<Call>) -> Array<Span<felt252>> {
let mut res = ArrayTrait::new();
loop {
match calls.pop_front() {
Option::Some(call) => {
let _res = _execute_single_call(call);
res.append(_res);
},
Option::None(_) => {
break ();
},
};
};
res
}

#[internal]
fn _execute_single_call(call: Call) -> Span<felt252> {
let Call{to, selector, calldata } = call;
starknet::call_contract_syscall(to, selector, calldata.span()).unwrap()
fn constructor(ref self: ContractState, public_key: felt252) {
self.account.initializer(public_key);
}
}
10 changes: 5 additions & 5 deletions src/account/error.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
mod AccountError {
const INVALID_CALLER: felt252 = 'Account: invalid caller';
const INVALID_TX_VERSION: felt252 = 'Account: invalid tx version';
const INVALID_SIGNATURE: felt252 = 'Account: invalid signature';
const UNAUTHORIZED: felt252 = 'Account: unauthorized';
mod AccountError { // const INVALID_CALLER: felt252 = 'Account: invalid caller';
// const INVALID_TX_VERSION: felt252 = 'Account: invalid tx version';
// const INVALID_SIGNATURE: felt252 = 'Account: invalid signature';
// const UNAUTHORIZED: felt252 = 'Account: unauthorized';
// const POSITION_NOT_FOUND: felt252 = 'Account: position not found';
}
Loading

0 comments on commit 57962fd

Please sign in to comment.