Skip to content

Commit

Permalink
Merge pull request #11 from Larkooo/burner-accounts
Browse files Browse the repository at this point in the history
feat: deploying burner accounts
  • Loading branch information
Larkooo authored Dec 21, 2023
2 parents 680abea + c169357 commit 01a0d3e
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 7 deletions.
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ url = "2.5.0"
tonic = "0.10.2"
futures-util = "0.3.29"
futures-channel = "0.3.29"
anyhow = "1.0.76"

[build-dependencies]
cbindgen = "0.24.0"
Expand Down
9 changes: 9 additions & 0 deletions dojo.h
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,9 @@ struct Result_____Account account_new(struct CJsonRpcClient *rpc,
struct FieldElement private_key,
const char *address);

struct Result_____Account account_deploy_burner(struct CJsonRpcClient *rpc,
struct Account *master_account);

struct FieldElement account_address(struct Account *account);

struct FieldElement account_chain_id(struct Account *account);
Expand All @@ -570,6 +573,12 @@ struct Result_bool account_execute_raw(struct Account *account,
const struct Call *calldata,
uintptr_t calldata_len);

struct FieldElement hash_get_contract_address(struct FieldElement class_hash,
struct FieldElement salt,
const struct FieldElement *constructor_calldata,
uintptr_t constructor_calldata_len,
struct FieldElement deployer_address);

void client_free(struct ToriiClient *t);

void jsonrpc_client_free(struct CJsonRpcClient *rpc);
Expand Down
17 changes: 13 additions & 4 deletions example/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,18 @@ int main()
printf("Failed to create account: %s\n", resAccount.err.message);
return 1;
}
Account *account = resAccount.ok;
Account *master_account = resAccount.ok;

FieldElement address = account_address(account);
Result_____Account resBurner = account_deploy_burner(provider, master_account);
if (resBurner.tag == Err_____Account)
{
printf("Failed to create burner: %s\n", resBurner.err.message);
return 1;
}

Account *burner = resBurner.ok;

FieldElement address = account_address(burner);
printf("New account: 0x");
for (size_t i = 0; i < 32; i++)
{
Expand Down Expand Up @@ -208,7 +217,7 @@ int main()

move.calldata.data[0] = moveLeft.ok;

Result_bool resSpawn = account_execute_raw(account, &spawn, 1);
Result_bool resSpawn = account_execute_raw(burner, &spawn, 1);
if (resSpawn.tag == Err_bool)
{
printf("Failed to execute call: %s\n", resSpawn.err.message);
Expand All @@ -217,7 +226,7 @@ int main()

sleep(5);

Result_bool resMove = account_execute_raw(account, &move, 1);
Result_bool resMove = account_execute_raw(burner, &move, 1);
if (resMove.tag == Err_bool)
{
printf("Failed to execute call: %s\n", resMove.err.message);
Expand Down
7 changes: 7 additions & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use starknet::macros::felt;
use starknet_crypto::FieldElement;

pub const KATANA_ACCOUNT_CLASS_HASH: FieldElement =
felt!("0x04d07e40e93398ed3c76981e72dd1fd22557a78ce36c0515f679e27f0bb5bc5f");
pub const UDC_ADDRESS: FieldElement =
felt!("0x41a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf");
87 changes: 86 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
mod constants;
mod types;
mod utils;

use futures_util::StreamExt;
use starknet::accounts::{Account as StarknetAccount, ExecutionEncoding, SingleOwnerAccount};
use starknet::core::utils::cairo_short_string_to_felt;
use starknet::core::utils::{
cairo_short_string_to_felt, get_contract_address, get_selector_from_name,
};
use starknet::providers::jsonrpc::HttpTransport;
use starknet::providers::{JsonRpcClient, Provider};
use starknet::signers::{LocalWallet, SigningKey, VerifyingKey};
Expand All @@ -16,6 +20,7 @@ use types::{
Account, BlockId, CArray, CJsonRpcClient, COption, Call, Entity, Error, KeysClause, Model,
Query, Result, Signature, ToriiClient, Ty, WorldMetadata,
};
use utils::watch_tx;

#[no_mangle]
#[allow(clippy::missing_safety_doc)]
Expand Down Expand Up @@ -379,6 +384,65 @@ pub unsafe extern "C" fn account_new(
Result::Ok(Box::into_raw(Box::new(Account(account))))
}

#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn account_deploy_burner(
rpc: *mut CJsonRpcClient,
master_account: *mut Account<'static>,
) -> Result<*mut Account<'static>> {
let signing_key = SigningKey::from_random();
let verifying_key = signing_key.verifying_key();
let address = get_contract_address(
verifying_key.scalar(),
constants::KATANA_ACCOUNT_CLASS_HASH,
&[verifying_key.scalar()],
FieldElement::ZERO,
);
let signer = LocalWallet::from_signing_key(signing_key);

let chain_id = (*master_account).0.chain_id();

let account = SingleOwnerAccount::new(
&(*rpc).0,
signer,
address,
chain_id,
ExecutionEncoding::Legacy,
);

// deploy the burner
let exec = (*master_account).0.execute(vec![starknet::accounts::Call {
to: constants::UDC_ADDRESS,
calldata: vec![
constants::KATANA_ACCOUNT_CLASS_HASH, // class_hash
verifying_key.scalar(), // salt
FieldElement::ZERO, // deployer_address
FieldElement::ONE, // constructor calldata length (1)
verifying_key.scalar(), // constructor calldata
],
selector: get_selector_from_name("deployContract").unwrap(),
}]);

let result = tokio::runtime::Runtime::new()
.unwrap()
.block_on(exec.send());

if let Err(e) = result {
return Result::Err(Error {
message: CString::new(e.to_string()).unwrap().into_raw(),
});
}

let result = result.unwrap();

tokio::runtime::Runtime::new()
.unwrap()
.block_on(watch_tx(&(*rpc).0, result.transaction_hash))
.unwrap();

Result::Ok(Box::into_raw(Box::new(Account(account))))
}

#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn account_address(account: *mut Account<'static>) -> types::FieldElement {
Expand Down Expand Up @@ -424,6 +488,27 @@ pub unsafe extern "C" fn account_execute_raw(
}
}

#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn hash_get_contract_address(
class_hash: types::FieldElement,
salt: types::FieldElement,
constructor_calldata: *const FieldElement,
constructor_calldata_len: usize,
deployer_address: types::FieldElement,
) -> types::FieldElement {
let class_hash = (&class_hash).into();
let salt = (&salt).into();
let constructor_calldata = unsafe {
std::slice::from_raw_parts(constructor_calldata, constructor_calldata_len).to_vec()
};
let deployer_address = (&deployer_address).into();

let address = get_contract_address(salt, class_hash, &constructor_calldata, deployer_address);

(&address).into()
}

// This function takes a raw pointer to ToriiClient as an argument.
// It checks if the pointer is not null. If it's not, it converts the raw pointer
// back into a Box<ToriiClient>, which gets dropped at the end of the scope,
Expand Down
30 changes: 30 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use anyhow::Result;

use starknet::{
core::types::StarknetError,
providers::{Provider, ProviderError},
};
use starknet_crypto::FieldElement;

pub async fn watch_tx<P>(provider: P, transaction_hash: FieldElement) -> Result<()>
where
P: Provider,
ProviderError: 'static,
{
loop {
// TODO: check with sequencer gateway if it's not confirmed after an extended period of
// time, as full nodes don't have access to failed transactions and would report them
// as `NotReceived`.
match provider.get_transaction_receipt(transaction_hash).await {
Ok(_) => {
// With JSON-RPC, once we get a receipt, the transaction must have been confirmed.
// Rejected transactions simply aren't available. This needs to be changed once we
// implement the sequencer fallback.

return Ok(());
}
Err(ProviderError::StarknetError(StarknetError::TransactionHashNotFound)) => {}
Err(err) => return Err(err.into()),
}
}
}

0 comments on commit 01a0d3e

Please sign in to comment.