Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: refactor and deploy #22

Merged
merged 4 commits into from
Oct 6, 2023
Merged
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
14 changes: 14 additions & 0 deletions addresses.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Registry:
- ClassHash: 0x7e616bcaaaaf4d22f756e0cabe0e312b6b8d753451ce0f4f6fc44936aae5ec7
- Contract: 0x57cf5b3ac51e9ab1735f0720425d3889ac500fc8deac6567ad8163fd210aa92

Account:
- ClassHash: 0xe01784f9a93db5171ed32eaee0610326969980ecbcc4325753428d8227b96b

Sample NFT:
- ClassHash: 0x6eac43a8dbb9431f51797486b08de7234420860d2051fa9bd8e9ce4b6203739
- Contract: 0x6aaf25616b3177b62b8aa2212cb19affb7cf52a62d97d260c50c53f13547111

Sample TBA:
- Contract: 0x01d6e561Eb81D2E91c626048e9e324526AD6c63034c8Ef7D605faF082A9673a3
- salt: 2030
46 changes: 40 additions & 6 deletions src/account/account.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use array::{ArrayTrait, SpanTrait};
use starknet::{account::Call, ContractAddress, ClassHash};

////////////////////////////////
// account interface
////////////////////////////////
#[starknet::interface]
trait IAccount<TContractState>{
fn get_public_key(self: @TContractState) -> felt252;
Expand All @@ -11,10 +14,13 @@ trait IAccount<TContractState>{
fn __validate_deploy__(self: @TContractState, class_hash:felt252, contract_address_salt:felt252, public_key:felt252) -> felt252;
fn __execute__(ref self: TContractState, calls:Array<Call>) -> Array<Span<felt252>>;
fn token(self:@TContractState) -> (ContractAddress, u256);
fn owner(ref self: TContractState, token_contract:ContractAddress, token_id:u256) -> ContractAddress;
fn owner(self: @TContractState, token_contract:ContractAddress, token_id:u256) -> ContractAddress;
fn upgrade(ref self: TContractState, implementation: ClassHash);
}

////////////////////////////////
// ERC721 interface
////////////////////////////////
#[starknet::interface]
trait IERC721<TContractState> {
fn balance_of(self: @TContractState, account: ContractAddress) -> u256;
Expand All @@ -33,6 +39,9 @@ trait IERC721<TContractState> {
fn token_uri(self: @TContractState, token_id: u256) -> felt252;
}

////////////////////////////////
// Account contract
////////////////////////////////
#[starknet::contract]
mod Account {
use starknet::{get_tx_info, get_caller_address, get_contract_address,ContractAddress, account::Call, call_contract_syscall, replace_class_syscall, ClassHash, SyscallResultTrait};
Expand All @@ -45,9 +54,9 @@ mod Account {

#[storage]
struct Storage{
_public_key: felt252,
_token_contract: ContractAddress,
_token_id: u256,
_public_key: felt252, // account signer key
_token_contract: ContractAddress, // contract address of NFT
_token_id: u256, // token ID of NFT
}

#[event]
Expand All @@ -56,6 +65,10 @@ mod Account {
Upgraded: Upgraded
}

/// @notice Emitted when the account upgrades to a new implementation
/// @param tokenContract the contract address of the NFT
/// @param tokenId the token ID of the NFT
/// @param implementation the upgraded account class hash
#[derive(Drop, starknet::Event)]
struct Upgraded {
tokenContract: ContractAddress,
Expand All @@ -73,16 +86,22 @@ mod Account {

#[external(v0)]
impl IAccountImpl of super::IAccount<ContractState>{
/// @notice get the account signer key
fn get_public_key(self: @ContractState) -> felt252{
self._public_key.read()
}

/// @notice sets a new public key for the account
/// @param new_public_key The new public key
fn set_public_key(ref self:ContractState, new_public_key:felt252){
self.assert_only_self();
self._public_key.write(new_public_key);
}

fn isValidSignature(self: @ContractState, hash:felt252, signature: Span<felt252>) -> bool {
/// @notice used for signature validation
/// @param hash The message hash
/// @param signature The signature to be validated
fn isValidSignature(self: @ContractState, hash: felt252, signature: Span<felt252>) -> bool {
self.is_valid_signature(hash, signature)
}

Expand All @@ -99,10 +118,14 @@ mod Account {
self.validate_transaction()
}

/// @notice validate an account transaction
/// @param calls an array of transactions to be executed
fn __validate__( ref self: ContractState, mut calls:Array<Call>) -> felt252{
self.validate_transaction()
}

/// @notice executes a transaction
/// @param calls an array of transactions to be executed
fn __execute__(ref self: ContractState, mut calls:Array<Call>) -> Array<Span<felt252>> {
let caller = get_caller_address();
assert(caller.is_zero(), 'invalid caller');
Expand All @@ -113,16 +136,22 @@ mod Account {
self._execute_calls(calls.span())
}

fn owner(ref self: ContractState, token_contract:ContractAddress, token_id:u256) -> ContractAddress {
/// @notice gets the token bound NFT owner
/// @param token_contract the contract address of the NFT
/// @param token_id the token ID of the NFT
fn owner(self: @ContractState, token_contract:ContractAddress, token_id:u256) -> ContractAddress {
IERC721Dispatcher { contract_address: token_contract }.owner_of(token_id)
}

/// @notice returns the contract address and token ID of the NFT
fn token(self: @ContractState) -> (ContractAddress, u256) {
let contract = self._token_contract.read();
let tokenId = self._token_id.read();
return (contract, tokenId);
}

/// @notice ugprades an account implementation
/// @param implementation the new class_hash
fn upgrade(ref self: ContractState, implementation: ClassHash) {
self.assert_only_self();
assert(!implementation.is_zero(), 'Invalid class hash');
Expand All @@ -139,12 +168,14 @@ mod Account {

#[generate_trait]
impl internalImpl of InternalTrait{
/// @notice check that caller is the token bound account
fn assert_only_self(ref self: ContractState){
let caller = get_caller_address();
let self = get_contract_address();
assert(self == caller, 'Account: unathorized');
}

/// @notice internal transaction for tx validation
fn validate_transaction(self: @ContractState) -> felt252 {
let tx_info = get_tx_info().unbox();
let tx_hash = tx_info.transaction_hash;
Expand All @@ -153,6 +184,7 @@ mod Account {
starknet::VALIDATED
}

/// @notice internal transaction for signature validation
fn is_valid_signature(self: @ContractState, hash:felt252, signature: Span<felt252>) -> bool {
let valid_length = signature.len() == 2_u32;
let public_key = self._public_key.read();
Expand All @@ -169,6 +201,8 @@ mod Account {
}
}

/// @notice internal transaction for executing transactions
/// @param calls An array of transactions to be executed
fn _execute_calls(ref self: ContractState, mut calls: Span<Call>) -> Array<Span<felt252>> {
let mut result: Array<Span<felt252>> = ArrayTrait::new();
let mut calls = calls;
Expand Down
39 changes: 32 additions & 7 deletions src/registry/registry.cairo
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
use core::array::SpanTrait;
use starknet::ContractAddress;

////////////////////////////////
// registry interface
////////////////////////////////
#[starknet::interface]
trait IRegistry<TContractState> {
fn create_account(ref self: TContractState, implementation_hash: felt252, public_key: felt252, token_contract: ContractAddress, token_id: u256, salt: felt252) -> ContractAddress;
fn get_account(self: @TContractState, implementation_hash: felt252, public_key: felt252, token_contract: ContractAddress, token_id: u256, salt: felt252) -> ContractAddress;
fn total_deployed_accounts(self: @TContractState, token_contract: ContractAddress, token_id: u256) -> u8;
}

////////////////////////////////
// ERC721 interface
////////////////////////////////
#[starknet::interface]
trait IERC721<TContractState> {
fn balance_of(self: @TContractState, account: ContractAddress) -> u256;
Expand All @@ -26,24 +32,24 @@ trait IERC721<TContractState> {
fn token_uri(self: @TContractState, token_id: u256) -> felt252;
}

////////////////////////////////
// Registry contract
////////////////////////////////
#[starknet::contract]
mod Registry {
use core::hash::HashStateTrait;
use starknet::{ContractAddress, get_caller_address, syscalls::call_contract_syscall, class_hash::ClassHash, class_hash::Felt252TryIntoClassHash, syscalls::deploy_syscall, SyscallResultTrait};
use zeroable::Zeroable;
use traits::TryInto;
use traits::Into;
use traits::{Into, TryInto};
use option::OptionTrait;
use array::ArrayTrait;
use array::SpanTrait;
use array::{ArrayTrait, SpanTrait};
use pedersen::PedersenTrait;

use super::IERC721DispatcherTrait;
use super::IERC721Dispatcher;
use super::{IERC721DispatcherTrait, IERC721Dispatcher};

#[storage]
struct Storage {
registry_deployed_accounts: LegacyMap<(ContractAddress, u256), u8>,
registry_deployed_accounts: LegacyMap<(ContractAddress, u256), u8>, // tracks no. of deployed accounts by registry for an NFT
}

#[event]
Expand All @@ -52,6 +58,10 @@ use starknet::{ContractAddress, get_caller_address, syscalls::call_contract_sysc
AccountCreated: AccountCreated
}

/// @notice Emitted when a new tokenbound account is deployed/created
/// @param account_address the deployed contract address of the tokenbound acccount
/// @param token_contract the contract address of the NFT
/// @param token_id the ID of the NFT
#[derive(Drop, starknet::Event)]
struct AccountCreated {
account_address: ContractAddress,
Expand All @@ -61,6 +71,12 @@ use starknet::{ContractAddress, get_caller_address, syscalls::call_contract_sysc

#[external(v0)]
impl IRegistryImpl of super::IRegistry<ContractState> {
/// @notice deploys a new tokenbound account for an NFT
/// @param implementation_hash the class hash of the reference account
/// @param public_key the signer key of the NFT owner
/// @param token_contract the contract address of the NFT
/// @param token_id the ID of the NFT
/// @param salt random salt for deployment
fn create_account(
ref self: ContractState,
implementation_hash: felt252,
Expand Down Expand Up @@ -92,6 +108,12 @@ use starknet::{ContractAddress, get_caller_address, syscalls::call_contract_sysc
account_address
}

/// @notice calculates the account address for an existing tokenbound account
/// @param implementation_hash the class hash of the reference account
/// @param public_key the signer key of the NFT owner
/// @param token_contract the contract address of the NFT
/// @param token_id the ID of the NFT
/// @param salt random salt for deployment
fn get_account(self: @ContractState, implementation_hash: felt252, public_key: felt252, token_contract: ContractAddress, token_id: u256, salt: felt252) -> ContractAddress {
let constructor_calldata_hash = PedersenTrait::new(0)
.update(public_key)
Expand All @@ -114,6 +136,9 @@ use starknet::{ContractAddress, get_caller_address, syscalls::call_contract_sysc
account_address.try_into().unwrap()
}

/// @notice returns the total no. of deployed tokenbound accounts for an NFT by the registry
/// @param token_contract the contract address of the NFT
/// @param token_id the ID of the NFT
fn total_deployed_accounts(self: @ContractState, token_contract: ContractAddress, token_id: u256) -> u8 {
self.registry_deployed_accounts.read((token_contract, token_id))
}
Expand Down
Loading