Skip to content

Commit

Permalink
chore: refactor, introduce context
Browse files Browse the repository at this point in the history
  • Loading branch information
Darlington02 committed Sep 7, 2024
1 parent d9aec27 commit 9964483
Show file tree
Hide file tree
Showing 10 changed files with 283 additions and 145 deletions.
80 changes: 57 additions & 23 deletions src/components/account/account.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ pub mod AccountComponent {
pub struct Storage {
account_token_contract: ContractAddress, // contract address of NFT
account_token_id: u256, // token ID of NFT
state: u256
context: Context, // account deployment details
state: u256 // account state
}

// *************************************************************************
Expand Down Expand Up @@ -65,6 +66,16 @@ pub mod AccountComponent {
pub response: Span<Span<felt252>>
}

// *************************************************************************
// STRUCTS
// *************************************************************************
#[derive(Copy, Drop, starknet::Store)]
struct Context {
registry: ContractAddress,
implementation_hash: felt252,
salt: felt252
}

// *************************************************************************
// ERRORS
// *************************************************************************
Expand All @@ -90,15 +101,6 @@ pub mod AccountComponent {
self._get_owner(token_contract, token_id)
}

/// @notice returns the root owner for nested tokenbound accounts
/// @param token_contract the contract address of the NFT
/// @param token_id the token ID of the NFT
fn get_root_owner(
self: @ComponentState<TContractState>, token_contract: ContractAddress, token_id: u256
) -> ContractAddress {
self._get_root_owner(token_contract, token_id)
}

/// @notice returns the contract address and token ID of the associated NFT
fn token(self: @ComponentState<TContractState>) -> (ContractAddress, u256, felt252) {
self._get_token()
Expand Down Expand Up @@ -133,13 +135,20 @@ pub mod AccountComponent {
fn initializer(
ref self: ComponentState<TContractState>,
token_contract: ContractAddress,
token_id: u256
token_id: u256,
registry: ContractAddress,
implementation_hash: felt252,
salt: felt252
) {
let owner = self._get_owner(token_contract, token_id);
assert(owner.is_non_zero(), Errors::UNAUTHORIZED);

// initialize account
self.account_token_contract.write(token_contract);
self.account_token_id.write(token_id);
self.context.write(Context { registry, implementation_hash, salt });

// emit event
self
.emit(
TBACreated {
Expand Down Expand Up @@ -208,21 +217,46 @@ pub mod AccountComponent {
Serde::<ContractAddress>::deserialize(ref address).unwrap()
}

/// @notice internal function for getting the root NFT owner
/// @notice internal function to retrieve deployment details of an account
fn _context(self: @ComponentState<TContractState>) -> (ContractAddress, felt252, felt252) {
let context = self.context.read();
(context.registry, context.implementation_hash, context.salt)
}

/// @notice internal function for checking if an account is a tokenbound account
/// @param token_contract contract address of NFT
// @param token_id token ID of NFT
// NB: This function aims for compatibility with all contracts (snake or camel case) but do
// not work as expected on mainnet as low level calls do not return err at the moment.
// Should work for contracts which implements CamelCase but not snake_case until starknet
// v0.15.
fn _get_root_owner(
self: @ComponentState<TContractState>, token_contract: ContractAddress, token_id: u256
) -> ContractAddress {
// TODO: implement logic to get root owner
/// @param token_id token ID of NFT
fn _is_tokenbound_account(
self: @ComponentState<TContractState>,
account: ContractAddress,
token_contract: ContractAddress,
token_id: u256,
registry: ContractAddress,
implementation: felt252,
salt: felt252
) -> bool {
let constructor_calldata_hash = PedersenTrait::new(0)
.update(token_contract.into())
.update(token_id.low.into())
.update(token_id.high.into())
.update(registry.into())
.update(implementation)
.update(salt)
.update(6)
.finalize();

1.try_into().unwrap()
}
let prefix: felt252 = 'STARKNET_CONTRACT_ADDRESS';
let account_address = PedersenTrait::new(0)
.update(prefix)
.update(0)
.update(salt)
.update(implementation)
.update(constructor_calldata_hash)
.update(5)
.finalize();

account_address.try_into().unwrap() == account
}

/// @notice internal transaction for returning the contract address and token ID of the NFT
fn _get_token(self: @ComponentState<TContractState>) -> (ContractAddress, u256, felt252) {
Expand Down
11 changes: 9 additions & 2 deletions src/components/presets/account_preset.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,15 @@ pub mod AccountPreset {
// CONSTRUCTOR
// *************************************************************************
#[constructor]
fn constructor(ref self: ContractState, token_contract: ContractAddress, token_id: u256) {
self.account.initializer(token_contract, token_id);
fn constructor(
ref self: ContractState,
token_contract: ContractAddress,
token_id: u256,
registry: ContractAddress,
implementation_hash: felt252,
salt: felt252
) {
self.account.initializer(token_contract, token_id, registry, implementation_hash, salt);
}

// *************************************************************************
Expand Down
53 changes: 6 additions & 47 deletions src/components/signatory/signatory.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub mod SignatoryComponent {
// *************************************************************************
use starknet::{get_caller_address, get_contract_address, ContractAddress};
use token_bound_accounts::components::account::account::AccountComponent;
use token_bound_accounts::components::account::account::AccountComponent::InternalImpl;
use token_bound_accounts::components::account::account::AccountComponent::AccountImpl;
use token_bound_accounts::components::permissionable::permissionable::PermissionableComponent;
use token_bound_accounts::components::permissionable::permissionable::PermissionableComponent::PermissionableImpl;
use token_bound_accounts::interfaces::ISRC6::{ISRC6Dispatcher, ISRC6DispatcherTrait};
Expand Down Expand Up @@ -45,10 +45,7 @@ pub mod SignatoryComponent {
self: @ComponentState<TContractState>, signer: ContractAddress
) -> bool {
let account = get_dep_component!(self, Account);
let (contract_address, token_id, _) = account._get_token();

// get owner
let owner = account._get_owner(contract_address, token_id);
let owner = account.owner();

// validate
if (signer == owner) {
Expand All @@ -58,44 +55,14 @@ pub mod SignatoryComponent {
}
}

/// @notice implements a signer validation where both NFT owner and the root owner (for
/// nested accounts) are valid signers.
/// @param signer the address to be validated
fn _base_and_root_signer_validation(
self: @ComponentState<TContractState>, signer: ContractAddress
) -> bool {
let account = get_dep_component!(self, Account);
let (contract_address, token_id, _) = account._get_token();

// get owner
let owner = account._get_owner(contract_address, token_id);
// get root owner
let root_owner = account._get_root_owner(contract_address, token_id);

// validate
if (signer == owner) {
return true;
} else if (signer == root_owner) {
return true;
} else {
return false;
}
}


/// @notice implements a more complex signer validation where NFT owner, root owner, and
/// permissioned addresses are valid signers.
/// @param signer the address to be validated
fn _permissioned_signer_validation(
self: @ComponentState<TContractState>, signer: ContractAddress
) -> bool {
let account = get_dep_component!(self, Account);
let (contract_address, token_id, _) = account._get_token();

// get owner
let owner = account._get_owner(contract_address, token_id);
// get root owner
let root_owner = account._get_root_owner(contract_address, token_id);
let owner = account.owner();

// check if signer has permissions
let permission = get_dep_component!(self, Permissionable);
Expand All @@ -104,8 +71,6 @@ pub mod SignatoryComponent {
// validate
if (signer == owner) {
return true;
} else if (signer == root_owner) {
return true;
} else if (is_permissioned) {
return true;
} else {
Expand All @@ -120,22 +85,16 @@ pub mod SignatoryComponent {
self: @ComponentState<TContractState>, hash: felt252, signature: Span<felt252>
) -> felt252 {
let account = get_dep_component!(self, Account);
let (contract_address, token_id, _) = account._get_token();
let owner = account._get_owner(contract_address, token_id);
let root_owner = account._get_root_owner(contract_address, token_id);
let owner = account.owner();

// validate signature length
let signature_length = signature.len();
assert(signature_length == 2_u32, Errors::INV_SIG_LEN);

let owner_account = ISRC6Dispatcher { contract_address: owner };
let root_owner_account = ISRC6Dispatcher { contract_address: root_owner };

// validate
let owner_account = ISRC6Dispatcher { contract_address: owner };
if (owner_account.is_valid_signature(hash, signature) == starknet::VALIDATED) {
return starknet::VALIDATED;
} else if (root_owner_account
.is_valid_signature(hash, signature) == starknet::VALIDATED) {
return starknet::VALIDATED;
} else {
return Errors::INVALID_SIGNATURE;
}
Expand Down
5 changes: 1 addition & 4 deletions src/interfaces/IAccount.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,8 @@ pub const TBA_INTERFACE_ID: felt252 =

#[starknet::interface]
pub trait IAccount<TContractState> {
fn token(self: @TContractState) -> (ContractAddress, u256, felt252);
fn owner(self: @TContractState) -> ContractAddress;
fn get_root_owner(
self: @TContractState, token_contract: ContractAddress, token_id: u256
) -> ContractAddress;
fn token(self: @TContractState) -> (ContractAddress, u256, felt252);
fn state(self: @TContractState) -> u256;
fn supports_interface(self: @TContractState, interface_id: felt252) -> bool;
}
17 changes: 13 additions & 4 deletions src/registry/registry.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ pub mod Registry {
use core::hash::HashStateTrait;
use core::pedersen::PedersenTrait;
use starknet::{
ContractAddress, get_caller_address, syscalls::{call_contract_syscall, deploy_syscall},
class_hash::ClassHash, SyscallResultTrait, storage::Map
ContractAddress, get_caller_address, get_contract_address,
syscalls::{call_contract_syscall, deploy_syscall}, class_hash::ClassHash,
SyscallResultTrait, storage::Map
};

use token_bound_accounts::interfaces::IERC721::{IERC721DispatcherTrait, IERC721Dispatcher};
Expand Down Expand Up @@ -72,7 +73,12 @@ pub mod Registry {
assert(owner == get_caller_address(), Errors::CALLER_IS_NOT_OWNER);

let mut constructor_calldata: Array<felt252> = array![
token_contract.into(), token_id.low.into(), token_id.high.into()
token_contract.into(),
token_id.low.into(),
token_id.high.into(),
get_contract_address().into(),
implementation_hash,
salt
];

let class_hash: ClassHash = implementation_hash.try_into().unwrap();
Expand Down Expand Up @@ -100,7 +106,10 @@ pub mod Registry {
.update(token_contract.into())
.update(token_id.low.into())
.update(token_id.high.into())
.update(3)
.update(get_contract_address().into())
.update(implementation_hash)
.update(salt)
.update(6)
.finalize();

let prefix: felt252 = 'STARKNET_CONTRACT_ADDRESS';
Expand Down
Loading

0 comments on commit 9964483

Please sign in to comment.