Skip to content
This repository has been archived by the owner on Jun 20, 2024. It is now read-only.

Commit

Permalink
updated fee mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
drspacemn committed Jun 14, 2022
1 parent e11c0d7 commit 3e86813
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 63 deletions.
228 changes: 228 additions & 0 deletions camp_3/contracts/cairo/account.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
# SPDX-License-Identifier: MIT
# OpenZeppelin Contracts for Cairo v0.1.0 (account/Account.cairo)

%lang starknet

from starkware.cairo.common.registers import get_fp_and_pc
from starkware.starknet.common.syscalls import get_contract_address
from starkware.cairo.common.signature import verify_ecdsa_signature
from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin
from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.memcpy import memcpy
from starkware.cairo.common.bool import TRUE
from starkware.starknet.common.syscalls import call_contract, get_caller_address, get_tx_info

#
# Storage Var
#
@storage_var
func Account_current_nonce() -> (res: felt):
end
@storage_var
func Account_public_key() -> (res: felt):
end
#
# Structs
#
struct Call:
member to: felt
member selector: felt
member calldata_len: felt
member calldata: felt*
end
# Tmp struct introduced while we wait for Cairo
# to support passing `[AccountCall]` to __execute__
struct AccountCallArray:
member to: felt
member selector: felt
member data_offset: felt
member data_len: felt
end
#
# Constructor
#
@constructor
func constructor{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(public_key: felt):
Account_public_key.write(public_key)
return ()
end

#
# Getters
#

@view
func get_public_key{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}() -> (res: felt):
let (res) = Account_public_key.read()
return (res=res)
end

@view
func get_nonce{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}() -> (res: felt):
let (res) = Account_current_nonce.read()
return (res=res)
end

#
# Setters
#

@external
func set_public_key{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(new_public_key: felt):
Account_public_key.write(new_public_key)
return ()
end

#
# Business logic
#

@view
func is_valid_signature{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr,
ecdsa_ptr: SignatureBuiltin*
}(
hash: felt,
signature_len: felt,
signature: felt*
) -> (is_valid: felt):
let (_public_key) = Account_public_key.read()

# This interface expects a signature pointer and length to make
# no assumption about signature validation schemes.
# But this implementation does, and it expects a (sig_r, sig_s) pair.
let sig_r = signature[0]
let sig_s = signature[1]

verify_ecdsa_signature(
message=hash,
public_key=_public_key,
signature_r=sig_r,
signature_s=sig_s)

return (is_valid=TRUE)
end

@external
func __execute__{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr,
ecdsa_ptr: SignatureBuiltin*
}(
call_array_len: felt,
call_array: AccountCallArray*,
calldata_len: felt,
calldata: felt*,
nonce: felt
) -> (response_len: felt, response: felt*):
alloc_locals

let (caller) = get_caller_address()
with_attr error_message("Account: no reentrant call"):
assert caller = 0
end
let (__fp__, _) = get_fp_and_pc()
let (tx_info) = get_tx_info()
let (_current_nonce) = Account_current_nonce.read()
# validate nonce
with_attr error_message("Account: nonce is invalid"):
assert _current_nonce = nonce
end
# TMP: Convert `AccountCallArray` to 'Call'.
let (calls : Call*) = alloc()
_from_call_array_to_call(call_array_len, call_array, calldata, calls)
let calls_len = call_array_len
# validate transaction
# let (is_valid) = is_valid_signature(tx_info.transaction_hash, tx_info.signature_len, tx_info.signature)
# with_attr error_message("Account: invalid signature"):
# assert is_valid = TRUE
# end
# bump nonce
Account_current_nonce.write(_current_nonce + 1)
# execute call
let (response : felt*) = alloc()
let (response_len) = _execute_list(calls_len, calls, response)
return (response_len=response_len, response=response)
end
func _execute_list{syscall_ptr: felt*}(
calls_len: felt,
calls: Call*,
response: felt*
) -> (response_len: felt):
alloc_locals

# if no more calls
if calls_len == 0:
return (0)
end

# do the current call
let this_call: Call = [calls]
let res = call_contract(
contract_address=this_call.to,
function_selector=this_call.selector,
calldata_size=this_call.calldata_len,
calldata=this_call.calldata
)
# copy the result in response
memcpy(response, res.retdata, res.retdata_size)
# do the next calls recursively
let (response_len) = _execute_list(calls_len - 1, calls + Call.SIZE, response + res.retdata_size)
return (response_len + res.retdata_size)
end

func _from_call_array_to_call{syscall_ptr: felt*}(
call_array_len: felt,
call_array: AccountCallArray*,
calldata: felt*,
calls: Call*
):
# if no more calls
if call_array_len == 0:
return ()
end

# parse the current call
assert [calls] = Call(
to=[call_array].to,
selector=[call_array].selector,
calldata_len=[call_array].data_len,
calldata=calldata + [call_array].data_offset
)
# parse the remaining calls recursively
_from_call_array_to_call(call_array_len - 1, call_array + AccountCallArray.SIZE, calldata, calls + Call.SIZE)
return ()
end
2 changes: 1 addition & 1 deletion camp_3/contracts/cairo/storage.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func get_mapping_store{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_c
end

@view
func get_multi_store() -> (value : (left : felt, right : felt)):
func get_multi_store{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}() -> (value : (left : felt, right : felt)):
let (value) = multi_store.read()
return (value)
end
Expand Down
5 changes: 2 additions & 3 deletions camp_3/contracts/python/account.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import json
import requests

from definitions import CALL_CONTRACT_ENDPOINT
from starkware.starknet.public.abi import get_selector_from_name

FEEDER_URL = "https://alpha4.starknet.io/feeder_gateway/call_contract"

def get_nonce(addr):
nonce_selector = get_selector_from_name("get_nonce")
nonce_resp = requests.request("POST", FEEDER_URL, data=json.dumps({
nonce_resp = requests.request("POST", CALL_CONTRACT_ENDPOINT, data=json.dumps({
"contract_address": hex(addr),
"entry_point_selector": hex(nonce_selector),
"calldata": [],
Expand Down
15 changes: 15 additions & 0 deletions camp_3/contracts/python/definitions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import os

CAIRO_PATH = os.getenv("CAIRO_PATH")

GOERLI_NODE=os.getenv("GOERLI_URL")
GOERLI_CORE_CONTRACTS=0xde29d060D45901Fb19ED6C6e959EB22d8626708e
GOERLI_VERIFIER=0xAB43bA48c9edF4C2C4bB01237348D1D7B28ef168
GOERLI_MEMORY_REGISTRY=0x743789ff2fF82Bfb907009C9911a7dA636D34FA7

FEEDER_URL = "https://alpha4.starknet.io/feeder_gateway"

ESTIMATE_FEE_ENDPOINT = FEEDER_URL+"/estimate_fee"
TRANSACTION_TRACE_ENDPOINT = FEEDER_URL+"/get_transaction_trace?transactionHash="
STATE_UPDATE_ENDPOINT = FEEDER_URL+"/get_state_update?blockNumber="
CALL_CONTRACT_ENDPOINT = FEEDER_URL+"/call_contract"
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,19 @@
import sys

sys.path.append('../../fees/python')
from fees import calculate_execution, WEI_CONVERT, COST_PER_WORD
from pathlib import Path
from starknet_py.contract import Contract
sys.path.append('../../contracts/python')
from execution import calculate_execution
from definitions import STATE_UPDATE_ENDPOINT
from fees import WEI_CONVERT, COST_PER_WORD
from starknet_py.net.client import Client


FEEDER_URL = "https://alpha4.starknet.io/feeder_gateway/get_state_update?blockNumber="
TX=0x38d6ed8a35638b3947fa0a6cee902127cbebcdaba5705010599bc6979eb2456
GOERLI_CORE_CONTRACTS=0xde29d060D45901Fb19ED6C6e959EB22d8626708e
GOERLI_VERIFIER=0xAB43bA48c9edF4C2C4bB01237348D1D7B28ef168
GOERLI_MEMORY_REGISTRY=0x743789ff2fF82Bfb907009C9911a7dA636D34FA7
GOERLI_NODE=os.getenv("GOERLI_URL")

client = Client("testnet")

# get contract address
#
# fetch transaction/contract data
#
demo_tx = client.get_transaction_sync(TX)
account_addr = "0x{:x}".format(demo_tx.transaction.contract_address)
contract_addr = "0x{:x}".format(demo_tx.transaction.calldata[1])
Expand All @@ -35,26 +32,37 @@
print("\tBlock Num:\t\t", demo_tx.block_number)

if "ACCEPTED_ON_L1" in str(demo_tx.status):
print("\nTransaction Info: ")
# fetch info
#
# fetch block data for 'gas_price'
#
block = client.get_block_sync(block_number=demo_tx.block_number)

#
# fetch state update/diffs for block
#
tx_receipt = client.get_transaction_receipt_sync(TX)
state_update = requests.request("GET", FEEDER_URL+str(demo_tx.block_number)).json()
state_update = requests.request("GET", STATE_UPDATE_ENDPOINT+str(demo_tx.block_number)).json()
account_state = state_update["state_diff"]["storage_diffs"][account_addr]
contract_state = state_update["state_diff"]["storage_diffs"][contract_addr]

#
# calculate fees
#
gas_price = block.gas_price/WEI_CONVERT
total_words = 2 * 2 + (2 * (len(account_state)+len(contract_state)))
calldata_fee = int(gas_price) * COST_PER_WORD * total_words
execution_fees = calculate_execution("0x{:x}".format(TX))

print("\tFees(Gas-{}): max - {} actual - {}".format(gas_price, demo_tx.transaction.max_fee/WEI_CONVERT, tx_receipt.actual_fee/WEI_CONVERT))
print("\tExecution Gas: {}\n".format(execution_fees))
print("\nTransaction Info: ")
print("\tFees(Gas-{:.02f}): max - {:.02f} actual - {:.02f}".format(gas_price, demo_tx.transaction.max_fee/WEI_CONVERT, tx_receipt.actual_fee/WEI_CONVERT))
print("\tExecution Fee: {}".format(execution_fees))
print("\tCalldata Fee: {}\n".format(calldata_fee))

print("\tAccount Update: ")
for i in range(len(account_state)):
print("\t\tkey {} -> value {}".format(account_state[i]["key"], int(account_state[i]["value"][2:], 16)))

print("\tContract Update: ")
print("\n\tContract Update: ")
for i in range(len(contract_state)):
print("\t\tkey {} -> value {}".format(contract_state[i]["key"], int(contract_state[i]["value"][2:], 16)))
print()
11 changes: 9 additions & 2 deletions camp_3/fees/python/execution.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import json
import requests
import sys

sys.path.append('../../contracts/python')
from definitions import TRANSACTION_TRACE_ENDPOINT

def _calc_inner_steps(resources):
if len(resources) == 0:
return 0
Expand All @@ -22,8 +29,8 @@ def _calc_inner_builtins(resources):
return out

def calculate_execution(tx_hash):
tx_trace_resp = requests.request("GET", TX_TRACE_URL+tx_hash).json()
tx_trace_resp = requests.request("GET", TRANSACTION_TRACE_ENDPOINT+tx_hash).json()

steps = _calc_inner_steps([tx_trace_resp["function_invocation"]])
builtins = _calc_inner_builtins([tx_trace_resp["function_invocation"]])
return (steps *.05), builtins
return (steps *.05) + builtins
Loading

0 comments on commit 3e86813

Please sign in to comment.