Skip to content

Commit

Permalink
Add cairo1 utils
Browse files Browse the repository at this point in the history
  • Loading branch information
ClementWalter committed Aug 28, 2024
1 parent 3055f3d commit fa62f10
Show file tree
Hide file tree
Showing 15 changed files with 114 additions and 41 deletions.
1 change: 1 addition & 0 deletions cairo1_contracts/utils/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
1 change: 1 addition & 0 deletions cairo1_contracts/utils/.tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
scarb 2.6.5
6 changes: 6 additions & 0 deletions cairo1_contracts/utils/Scarb.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "library_call"
version = "0.1.0"
12 changes: 12 additions & 0 deletions cairo1_contracts/utils/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "library_call"
version = "0.1.0"
edition = "2023_11"

[dependencies]
starknet = "2.6.4"

[[target.starknet-contract]]
casm = true
sierra = true
casm-add-pythonic-hints = true
39 changes: 39 additions & 0 deletions cairo1_contracts/utils/src/balance_sender.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use core::starknet::{get_caller_address, ContractAddress};


#[starknet::interface]
pub trait IERC20<TState> {
fn name(self: @TState) -> felt252;
fn symbol(self: @TState) -> felt252;
fn decimals(self: @TState) -> u8;
fn total_supply(self: @TState) -> u256;
fn balance_of(self: @TState, account: ContractAddress) -> u256;
fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256;
fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool;
fn transfer_from(
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;
fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool;
}

#[starknet::contract]
pub mod BalanceSender {
use core::starknet::{get_caller_address, ContractAddress, ClassHash, get_contract_address, SyscallResult};
use super::{IERC20Dispatcher, IERC20DispatcherTrait};
use core::starknet::syscalls::{replace_class_syscall};

#[storage]
struct Storage {}

#[external(v0)]
fn send_balance(self: @ContractState, token_address: ContractAddress, recipient: ContractAddress) -> bool {
let erc20_dispatcher = IERC20Dispatcher { contract_address: token_address };
let balance = erc20_dispatcher.balance_of(get_contract_address());
erc20_dispatcher.transfer(recipient, balance)
}

#[external(v0)]
fn replace_class(ref self: ContractState, new_class: ClassHash) -> SyscallResult<()>{
replace_class_syscall(new_class)
}
}
3 changes: 3 additions & 0 deletions cairo1_contracts/utils/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod universal_library_caller;

mod balance_sender;
28 changes: 28 additions & 0 deletions cairo1_contracts/utils/src/universal_library_caller.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use starknet::{
SyscallResult, storage_access::StorageAddress, class_hash::ClassHash,
};


#[starknet::interface]
pub trait IUniversalLibraryCaller<TContractState> {
fn library_call(self: @TContractState, class_hash: ClassHash, function_selector: felt252, calldata: Span<felt252>) -> SyscallResult<Span<felt252>>;
}

#[starknet::contract]
pub mod UniversalLibraryCaller {
use starknet::syscalls::library_call_syscall;
use starknet::{
SyscallResult, storage_access::StorageAddress, class_hash::ClassHash,
};


#[storage]
struct Storage {}

#[abi(embed_v0)]
impl UniversalLibraryCallerImpl of super::IUniversalLibraryCaller<ContractState> {
fn library_call(self: @ContractState, class_hash: ClassHash, function_selector: felt252, calldata: Span<felt252>) -> SyscallResult<Span<felt252>> {
library_call_syscall(class_hash, function_selector, calldata)
}
}
}
6 changes: 4 additions & 2 deletions deployments/starknet-sepolia/declarations.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@
"MockPragmaOracle": "0x675f00328ff84f127d71b179b3f3a3a06ce8432054770cddd5729c8d62866da",
"StarknetToken": "0x27dd8ce628866f1544202ae06ec57b3c9b1f775d5f7c2797de7aa1586ecf693",
"ERC20": "0x3c5ee4bc12f4247cd8071150c3f5d9bee71f40b0ef7aeae59f468d898f60933",
"kakarot": "0x3f9e4ac97c943181453ce74f1fd1c163c154c40d9cbbbe5c2453512ee1a86e6"
}
"kakarot": "0x3f9e4ac97c943181453ce74f1fd1c163c154c40d9cbbbe5c2453512ee1a86e6",
"UniversalLibraryCaller": "0x5e84816dcbfd11581d8d5160af5754a4adc71ab35a0c0aaa053773f61838627",
"BalanceSender": "0x2cc118f56b9d3ad311900db5254f3dca75fbf24de3b68ee670a0fb3691ac5b3"
}
5 changes: 5 additions & 0 deletions deployments/starknet-sepolia/deployments.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,10 @@
"address": "0x17e64c92b06da9a331da9fd333a683a33019ae2a393254caf332d4158edc74d",
"tx": "0x3d6b91602c1e290bc65c6f85751f5ea156cf982d01c6bf1ea694d7398a9d5a5",
"artifact": "build/ssj/contracts_MockPragmaOracle"
},
"UniversalLibraryCaller": {
"address": "0x01e12ea32baf68b1e11c1ce32595d3a61a22ccdcbc67f94c77268b6ce99fa6d4",
"tx": "0x3d6b91602c1e290bc65c6f85751f5ea156cf982d01c6bf1ea694d7398a9d5a5",
"artifact": "cairo1_contracts/utils/target/dev/library_call_UniversalLibraryCaller"
}
}
1 change: 1 addition & 0 deletions kakarot_scripts/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ class ArtifactType(Enum):
{"contract_name": "StarknetToken", "cairo_version": ArtifactType.cairo1},
{"contract_name": "ERC20", "cairo_version": ArtifactType.cairo0},
{"contract_name": "kakarot", "cairo_version": ArtifactType.cairo0},
{"contract_name": "UniversalLibraryCaller", "cairo_version": ArtifactType.cairo1},
]

# PRE-EIP155 TX
Expand Down
8 changes: 4 additions & 4 deletions kakarot_scripts/deploy_kakarot.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,17 +155,17 @@ async def main():

if NETWORK["type"] is (NetworkType.DEV or NetworkType.STAGING):
bridge = await deploy_evm("CairoPrecompiles", "EthStarknetBridge")
evm_deployments["Bridge"] = {
"address": int(bridge.address, 16),
"starknet_address": bridge.starknet_address,
}
await invoke(
"kakarot",
"set_authorized_cairo_precompile_caller",
int(bridge.address, 16),
1,
)
await invoke("kakarot", "set_coinbase", int(bridge.address, 16))
evm_deployments["bridge"] = {
"address": int(bridge.address, 16),
"starknet_address": bridge.starknet_address,
}
await invoke("kakarot", "set_base_fee", 1)

dump_evm_deployments(evm_deployments)
Expand Down
7 changes: 6 additions & 1 deletion kakarot_scripts/utils/starknet.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,9 @@ def get_artifact(contract_name, cairo_version=None):
if cairo_version is None:
cairo_version = get_artifact_version(contract_name)
if cairo_version == ArtifactType.cairo1:
if artifacts := list(Path("cairo1_contracts").glob(f"**/*{contract_name}*")):
return artifacts[0].with_suffix("").with_suffix(""), ArtifactType.cairo1

return (BUILD_DIR_SSJ / f"contracts_{contract_name}", ArtifactType.cairo1)

return (
Expand Down Expand Up @@ -286,7 +289,9 @@ def is_fixture_contract(contract_name):
def get_artifact_version(contract_name):
cairo_0 = contract_name in set(CONTRACTS_FIXTURES).union(set(CONTRACTS))
cairo_1 = any(
contract_name in str(artifact) for artifact in BUILD_DIR_SSJ.glob("*.json")
contract_name in str(artifact)
for artifact in list(BUILD_DIR_SSJ.glob("*.json"))
+ list(Path("cairo1_contracts").glob("**/*.json"))
)
if cairo_0 and cairo_1:
raise ValueError(f"Contract {contract_name} is ambiguous")
Expand Down
2 changes: 0 additions & 2 deletions solidity_contracts/src/CairoPrecompiles/EthStarknetBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ contract EthStarknetBridge {
transferCallData[1] = uint256(amountLow);
transferCallData[2] = uint256(amountHigh);

// TODO: fine tune the 100_000 gas limit
require(gasleft() > 100_000, "Not enough gas to call Eth Cairo contract");
starknetEth.delegatecallCairo(TRANSFER_SELECTOR, transferCallData);
}
}
33 changes: 3 additions & 30 deletions tests/end_to_end/Solmate/test_erc20.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,36 +229,9 @@ async def test_should_permit(self, erc_20, owner, other):
}
]
assert await erc_20.allowance(owner.address, other.address) == TEST_SUPPLY
assert await erc_20.nonces(owner.address) == 1
assert await erc_20.nonces(owner.address) == nonce + 1

async def test_permit_should_fail_with_bad_nonce(self, erc_20, owner, other):
bad_nonce = 1
deadline = 2**256 - 1
digest = get_approval_digest(
"Kakarot Token",
erc_20.address,
{
"owner": owner.address,
"spender": other.address,
"value": MAX_INT,
},
bad_nonce,
deadline,
)
v, r, s = ec_sign(digest, owner.private_key)
with evm_error("INVALID_SIGNER"):
await erc_20.permit(
owner.address,
other.address,
TEST_SUPPLY,
deadline,
v,
r,
s,
caller_eoa=owner.starknet_contract,
)

async def test_permit_should_fail_with_bad_deadline(
async def test_should_fail_with_bad_deadline(
self, erc_20, block_timestamp, owner, other
):
nonce = await erc_20.nonces(owner.address)
Expand Down Expand Up @@ -289,7 +262,7 @@ async def test_permit_should_fail_with_bad_deadline(
caller_eoa=owner.starknet_contract,
)

async def test_permit_should_fail_on_replay(self, erc_20, owner, other):
async def test_should_fail_on_replay(self, erc_20, owner, other):
nonce = await erc_20.nonces(owner.address)
deadline = 2**256 - 1
digest = get_approval_digest(
Expand Down
3 changes: 1 addition & 2 deletions tests/end_to_end/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ async def _factory(amount=0):
"CairoPrecompiles", "EthStarknetBridge", address=bridge_address
)
gas_price = (await call("kakarot", "get_base_fee")).base_fee
# Hard coded gas limit according to the require(gasleft > 100k) in the bridge contract
gas_limit = 150000
gas_limit = 40_000
tx_cost = gas_limit * gas_price
for wallet in deployed:
balance = await eth_balance_of(wallet.address)
Expand Down

0 comments on commit fa62f10

Please sign in to comment.